Skip to content

Commit c3df045

Browse files
committed
feat(flowchart): enable editing of existing nodes via double-click
1 parent 003c145 commit c3df045

1 file changed

Lines changed: 85 additions & 26 deletions

File tree

‎src/pages/FlowchartPage.tsx‎

Lines changed: 85 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ function CustomNode({ data }: { data: FlowNodeData }) {
8686
<Badge
8787
variant="outline"
8888
className={`mt-2 text-xs ${data.strength === 'strong'
89-
? 'border-green-300 text-green-100'
90-
: data.strength === 'weak'
91-
? 'border-red-300 text-red-100'
92-
: 'border-yellow-300 text-yellow-100'
89+
? 'border-green-300 text-green-100'
90+
: data.strength === 'weak'
91+
? 'border-red-300 text-red-100'
92+
: 'border-yellow-300 text-yellow-100'
9393
}`}
9494
>
9595
{data.strength}
@@ -160,6 +160,7 @@ export default function FlowchartPage() {
160160
const [newNodeType, setNewNodeType] = useState<NodeType>('contention');
161161
const [newNodeStrength, setNewNodeStrength] = useState<'strong' | 'moderate' | 'weak'>('moderate');
162162
const [selectedNode, setSelectedNode] = useState<string | null>(null);
163+
const [editingNodeId, setEditingNodeId] = useState<string | null>(null);
163164

164165
const onConnect = useCallback(
165166
(connection: Connection) => {
@@ -177,26 +178,70 @@ export default function FlowchartPage() {
177178
[setEdges]
178179
);
179180

180-
const addNode = useCallback(() => {
181+
const handleSaveNode = useCallback(() => {
181182
if (!newNodeLabel.trim()) return;
182183

183-
const newNode: Node<FlowNodeData> = {
184-
id: `node-${Date.now()}`,
185-
type: 'custom',
186-
position: { x: Math.random() * 400 + 100, y: Math.random() * 300 + 100 },
187-
data: {
188-
label: newNodeLabel,
189-
description: newNodeDescription,
190-
type: newNodeType,
191-
strength: newNodeType === 'contention' ? newNodeStrength : undefined,
192-
},
193-
};
184+
if (editingNodeId) {
185+
// Update existing node
186+
setNodes((nds) =>
187+
nds.map((node) => {
188+
if (node.id === editingNodeId) {
189+
return {
190+
...node,
191+
data: {
192+
...node.data,
193+
label: newNodeLabel,
194+
description: newNodeDescription,
195+
type: newNodeType,
196+
strength: newNodeType === 'contention' ? newNodeStrength : undefined,
197+
},
198+
};
199+
}
200+
return node;
201+
})
202+
);
203+
setEditingNodeId(null);
204+
} else {
205+
// Add new node
206+
const newNode: Node<FlowNodeData> = {
207+
id: `node-${Date.now()}`,
208+
type: 'custom',
209+
position: { x: Math.random() * 400 + 100, y: Math.random() * 300 + 100 },
210+
data: {
211+
label: newNodeLabel,
212+
description: newNodeDescription,
213+
type: newNodeType,
214+
strength: newNodeType === 'contention' ? newNodeStrength : undefined,
215+
},
216+
};
217+
setNodes((nds) => [...nds, newNode]);
218+
}
194219

195-
setNodes((nds) => [...nds, newNode]);
220+
// Reset form
196221
setNewNodeLabel('');
197222
setNewNodeDescription('');
198223
setIsAddDialogOpen(false);
199-
}, [newNodeLabel, newNodeDescription, newNodeType, newNodeStrength, setNodes]);
224+
}, [newNodeLabel, newNodeDescription, newNodeType, newNodeStrength, editingNodeId, setNodes]);
225+
226+
const openAddDialog = () => {
227+
setEditingNodeId(null);
228+
setNewNodeLabel('');
229+
setNewNodeDescription('');
230+
setNewNodeType('contention');
231+
setNewNodeStrength('moderate');
232+
setIsAddDialogOpen(true);
233+
};
234+
235+
const openEditDialog = useCallback((node: Node<FlowNodeData>) => {
236+
setEditingNodeId(node.id);
237+
setNewNodeLabel(node.data.label);
238+
setNewNodeDescription(node.data.description || '');
239+
setNewNodeType(node.data.type);
240+
if (node.data.strength) {
241+
setNewNodeStrength(node.data.strength);
242+
}
243+
setIsAddDialogOpen(true);
244+
}, []);
200245

201246
const deleteSelectedNode = useCallback(() => {
202247
if (!selectedNode || selectedNode === 'thesis') return;
@@ -249,15 +294,15 @@ export default function FlowchartPage() {
249294
<div className="flex gap-2">
250295
<Dialog open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen}>
251296
<DialogTrigger asChild>
252-
<Button variant="outline" size="sm">
297+
<Button variant="outline" size="sm" onClick={openAddDialog}>
253298
<Plus className="h-4 w-4 mr-2" />
254299
Add Node
255300
</Button>
256301
</DialogTrigger>
257302
<DialogContent>
258303
<DialogHeader>
259-
<DialogTitle>Add Argument Node</DialogTitle>
260-
<DialogDescription>Create a new node in your flowchart</DialogDescription>
304+
<DialogTitle>{editingNodeId ? 'Edit Argument Node' : 'Add Argument Node'}</DialogTitle>
305+
<DialogDescription>{editingNodeId ? 'Update the details of your node' : 'Create a new node in your flowchart'}</DialogDescription>
261306
</DialogHeader>
262307
<div className="space-y-4 py-4">
263308
<div className="space-y-2">
@@ -307,16 +352,29 @@ export default function FlowchartPage() {
307352
)}
308353
</div>
309354
<DialogFooter>
310-
<Button onClick={addNode}>Add Node</Button>
355+
<Button onClick={handleSaveNode}>{editingNodeId ? 'Save Changes' : 'Add Node'}</Button>
311356
</DialogFooter>
312357
</DialogContent>
313358
</Dialog>
314359

315360
{selectedNode && selectedNode !== 'thesis' && (
316-
<Button variant="destructive" size="sm" onClick={deleteSelectedNode}>
317-
<Trash2 className="h-4 w-4 mr-2" />
318-
Delete
319-
</Button>
361+
<>
362+
<Button
363+
variant="outline"
364+
size="sm"
365+
onClick={() => {
366+
const node = nodes.find(n => n.id === selectedNode);
367+
if (node) openEditDialog(node);
368+
}}
369+
>
370+
<GitBranch className="h-4 w-4 mr-2" />
371+
Edit
372+
</Button>
373+
<Button variant="destructive" size="sm" onClick={deleteSelectedNode}>
374+
<Trash2 className="h-4 w-4 mr-2" />
375+
Delete
376+
</Button>
377+
</>
320378
)}
321379

322380
<Button variant="outline" size="sm" onClick={exportToPng}>
@@ -355,6 +413,7 @@ export default function FlowchartPage() {
355413
onEdgesChange={onEdgesChange}
356414
onConnect={onConnect}
357415
onNodeClick={(_, node) => setSelectedNode(node.id)}
416+
onNodeDoubleClick={(_, node) => openEditDialog(node as Node<FlowNodeData>)}
358417
onPaneClick={() => setSelectedNode(null)}
359418
nodeTypes={nodeTypes}
360419
fitView

0 commit comments

Comments
 (0)