feat: [US-021] - Add dialogue node from toolbar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9d46aa744d
commit
99e2d5aa15
|
|
@ -10,6 +10,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supabase/ssr": "^0.8.0",
|
"@supabase/ssr": "^0.8.0",
|
||||||
"@supabase/supabase-js": "^2.91.0",
|
"@supabase/supabase-js": "^2.91.0",
|
||||||
|
"nanoid": "^5.1.6",
|
||||||
"next": "16.1.4",
|
"next": "16.1.4",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
|
|
@ -5511,9 +5512,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "5.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
@ -5522,10 +5523,10 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^18 || >=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/napi-postinstall": {
|
"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": {
|
"node_modules/next/node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
|
|
@ -5928,6 +5947,25 @@
|
||||||
"node": "^10 || ^12 || >=14"
|
"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": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supabase/ssr": "^0.8.0",
|
"@supabase/ssr": "^0.8.0",
|
||||||
"@supabase/supabase-js": "^2.91.0",
|
"@supabase/supabase-js": "^2.91.0",
|
||||||
|
"nanoid": "^5.1.6",
|
||||||
"next": "16.1.4",
|
"next": "16.1.4",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ import ReactFlow, {
|
||||||
Controls,
|
Controls,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
|
useReactFlow,
|
||||||
|
ReactFlowProvider,
|
||||||
addEdge,
|
addEdge,
|
||||||
Connection,
|
Connection,
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
Edge,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
} from 'reactflow'
|
} from 'reactflow'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
import 'reactflow/dist/style.css'
|
import 'reactflow/dist/style.css'
|
||||||
import Toolbar from '@/components/editor/Toolbar'
|
import Toolbar from '@/components/editor/Toolbar'
|
||||||
import DialogueNode from '@/components/editor/nodes/DialogueNode'
|
import DialogueNode from '@/components/editor/nodes/DialogueNode'
|
||||||
|
|
@ -45,9 +48,8 @@ function toReactFlowEdges(edges: FlowchartEdge[]): Edge[] {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FlowchartEditor({
|
// Inner component that uses useReactFlow hook
|
||||||
initialData,
|
function FlowchartEditorInner({ initialData }: FlowchartEditorProps) {
|
||||||
}: FlowchartEditorProps) {
|
|
||||||
// Define custom node types - memoized to prevent re-renders
|
// Define custom node types - memoized to prevent re-renders
|
||||||
const nodeTypes: NodeTypes = useMemo(
|
const nodeTypes: NodeTypes = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|
@ -56,6 +58,8 @@ export default function FlowchartEditor({
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { getViewport } = useReactFlow()
|
||||||
|
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState(
|
const [nodes, setNodes, onNodesChange] = useNodesState(
|
||||||
toReactFlowNodes(initialData.nodes)
|
toReactFlowNodes(initialData.nodes)
|
||||||
)
|
)
|
||||||
|
|
@ -68,10 +72,27 @@ export default function FlowchartEditor({
|
||||||
[setEdges]
|
[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(() => {
|
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(() => {
|
const handleAddChoice = useCallback(() => {
|
||||||
// TODO: Implement in US-023
|
// TODO: Implement in US-023
|
||||||
|
|
@ -120,3 +141,12 @@ export default function FlowchartEditor({
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Outer wrapper component with ReactFlowProvider
|
||||||
|
export default function FlowchartEditor(props: FlowchartEditorProps) {
|
||||||
|
return (
|
||||||
|
<ReactFlowProvider>
|
||||||
|
<FlowchartEditorInner {...props} />
|
||||||
|
</ReactFlowProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue