import { useCallback, useEffect, useState } from 'react' import { ReactFlow, Background, Controls, ControlButton, BackgroundVariant, ConnectionMode, SelectionMode, useReactFlow, type Node, type Edge, type Connection, } from '@xyflow/react' import { MousePointer2, Hand } from 'lucide-react' import '@xyflow/react/dist/style.css' import { useCanvasStore } from '@/stores/canvasStore' import { useThemeStore } from '@/stores/themeStore' import { THEMES } from '@/utils/themes' import { nodeTypes } from './nodes/nodeTypes' import { edgeTypes } from './edges/edgeTypes' import { SearchBar } from './SearchBar' import { AlignmentGuides } from './AlignmentGuides' import { useAlignmentGuides } from '@/hooks/useAlignmentGuides' import type { NodeData, EdgeData } from '@/types' interface CanvasContainerProps { onConnect?: (connection: Connection) => void onEdgeDoubleClick?: (edge: Edge) => void onNodeDoubleClick?: (node: Node) => void onNodeDragStart?: () => void onOpenPending?: (deviceId: string) => void } export function CanvasContainer({ onConnect: onConnectProp, onEdgeDoubleClick, onNodeDoubleClick, onNodeDragStart, onOpenPending }: CanvasContainerProps) { const [lassoMode, setLassoMode] = useState(true) const { nodes, edges, onNodesChange, onEdgesChange, setSelectedNode, snapshotHistory, fitViewPending, clearFitViewPending, } = useCanvasStore() const { fitView } = useReactFlow() // Fit view after canvas loads (fitViewPending is set by loadCanvas) useEffect(() => { if (!fitViewPending || nodes.length === 0) return const id = setTimeout(() => { fitView({ padding: 0.12, duration: 350 }) clearFitViewPending() }, 50) return () => clearTimeout(id) }, [fitViewPending, nodes.length, fitView, clearFitViewPending]) const activeTheme = useThemeStore((s) => s.activeTheme) const theme = THEMES[activeTheme] const onNodeClick = useCallback((e: React.MouseEvent, node: Node) => { if (e.ctrlKey || e.metaKey) { setSelectedNode(null) } else { setSelectedNode(node.id) } }, [setSelectedNode]) const onPaneClick = useCallback(() => { setSelectedNode(null) }, [setSelectedNode]) const handleEdgeDoubleClick = useCallback((_: React.MouseEvent, edge: Edge) => { onEdgeDoubleClick?.(edge) }, [onEdgeDoubleClick]) const handleNodeDoubleClick = useCallback((_: React.MouseEvent, node: Node) => { onNodeDoubleClick?.(node) }, [onNodeDoubleClick]) const handleBeforeDelete = useCallback(async () => { snapshotHistory() return true }, [snapshotHistory]) const isValidConnection = useCallback( (connection: { source: string | null; target: string | null }) => connection.source !== connection.target, [] ) const { guides, onNodeDrag, onNodeDragStop } = useAlignmentGuides() return (
setLassoMode((m) => !m)} title={lassoMode ? 'Switch to pan mode (Space to pan)' : 'Switch to lasso mode'} > {lassoMode ? : }
) }