From 99e2d5aa1503716222fa0340aeec83242103b9e5 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Wed, 21 Jan 2026 12:50:20 -0300 Subject: [PATCH] feat: [US-021] - Add dialogue node from toolbar Co-Authored-By: Claude Opus 4.5 --- package-lock.json | 48 +++++++++++++++++-- package.json | 1 + .../editor/[projectId]/FlowchartEditor.tsx | 42 +++++++++++++--- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8e8643..1e138bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@supabase/ssr": "^0.8.0", "@supabase/supabase-js": "^2.91.0", + "nanoid": "^5.1.6", "next": "16.1.4", "react": "19.2.3", "react-dom": "19.2.3", @@ -5511,9 +5512,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", "funding": [ { "type": "github", @@ -5522,10 +5523,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/napi-postinstall": { @@ -5604,6 +5605,24 @@ } } }, + "node_modules/next/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5928,6 +5947,25 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index ba6a8ec..839d947 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@supabase/ssr": "^0.8.0", "@supabase/supabase-js": "^2.91.0", + "nanoid": "^5.1.6", "next": "16.1.4", "react": "19.2.3", "react-dom": "19.2.3", diff --git a/src/app/editor/[projectId]/FlowchartEditor.tsx b/src/app/editor/[projectId]/FlowchartEditor.tsx index 79e15f5..3ffe3e8 100644 --- a/src/app/editor/[projectId]/FlowchartEditor.tsx +++ b/src/app/editor/[projectId]/FlowchartEditor.tsx @@ -7,12 +7,15 @@ import ReactFlow, { Controls, useNodesState, useEdgesState, + useReactFlow, + ReactFlowProvider, addEdge, Connection, Node, Edge, NodeTypes, } from 'reactflow' +import { nanoid } from 'nanoid' import 'reactflow/dist/style.css' import Toolbar from '@/components/editor/Toolbar' import DialogueNode from '@/components/editor/nodes/DialogueNode' @@ -45,9 +48,8 @@ function toReactFlowEdges(edges: FlowchartEdge[]): Edge[] { })) } -export default function FlowchartEditor({ - initialData, -}: FlowchartEditorProps) { +// Inner component that uses useReactFlow hook +function FlowchartEditorInner({ initialData }: FlowchartEditorProps) { // Define custom node types - memoized to prevent re-renders const nodeTypes: NodeTypes = useMemo( () => ({ @@ -56,6 +58,8 @@ export default function FlowchartEditor({ [] ) + const { getViewport } = useReactFlow() + const [nodes, setNodes, onNodesChange] = useNodesState( toReactFlowNodes(initialData.nodes) ) @@ -68,10 +72,27 @@ export default function FlowchartEditor({ [setEdges] ) - // Placeholder handlers - functionality will be added in future stories + // Get center position of current viewport for placing new nodes + const getViewportCenter = useCallback(() => { + const viewport = getViewport() + // Calculate center based on viewport dimensions (assume ~800x600 visible area) + // Adjust based on zoom level + const centerX = (-viewport.x + 400) / viewport.zoom + const centerY = (-viewport.y + 300) / viewport.zoom + return { x: centerX, y: centerY } + }, [getViewport]) + + // Add dialogue node at viewport center const handleAddDialogue = useCallback(() => { - // TODO: Implement in US-021 - }, []) + const position = getViewportCenter() + const newNode: Node = { + id: nanoid(), + type: 'dialogue', + position, + data: { speaker: '', text: '' }, + } + setNodes((nodes) => [...nodes, newNode]) + }, [getViewportCenter, setNodes]) const handleAddChoice = useCallback(() => { // TODO: Implement in US-023 @@ -120,3 +141,12 @@ export default function FlowchartEditor({ ) } + +// Outer wrapper component with ReactFlowProvider +export default function FlowchartEditor(props: FlowchartEditorProps) { + return ( + + + + ) +}