Spaces:
Sleeping
Sleeping
| import { useEffect, useMemo } from "react"; | |
| import ReactFlow, { | |
| Background, | |
| BackgroundVariant, | |
| Controls, | |
| MiniMap, | |
| useReactFlow, | |
| ReactFlowProvider, | |
| type Node, | |
| type NodeMouseHandler, | |
| } from "reactflow"; | |
| import "reactflow/dist/style.css"; | |
| import "./graph-overrides.css"; | |
| import { layoutGraph } from "../lib/layout"; | |
| import type { IRGraph, IRNode } from "../types"; | |
| import { nodeTypes } from "./CustomNodes"; | |
| interface Props { | |
| ir: IRGraph; | |
| onSelectNode: (node: IRNode | null) => void; | |
| } | |
| function GraphViewInner({ ir, onSelectNode }: Props) { | |
| const { nodes, edges } = useMemo(() => layoutGraph(ir), [ir]); | |
| const { fitView } = useReactFlow(); | |
| // Re-fit the camera every time the underlying graph topology changes | |
| // (e.g. a new model loaded, or the granularity slider collapsed clusters). | |
| // Without this, the viewport stays where the previous graph was and the | |
| // user can end up staring at an empty panel. | |
| useEffect(() => { | |
| const id = window.setTimeout(() => { | |
| fitView({ duration: 200, padding: 0.15 }); | |
| }, 50); | |
| return () => window.clearTimeout(id); | |
| }, [nodes.length, edges.length, fitView]); | |
| const handleNodeClick: NodeMouseHandler = (_e, node) => { | |
| const irNode = (node.data as { irNode?: IRNode }).irNode ?? null; | |
| onSelectNode(irNode); | |
| }; | |
| const handlePaneClick = () => onSelectNode(null); | |
| const nodeColor = (n: Node) => | |
| (n.data as { color?: string }).color ?? "#6477a8"; | |
| return ( | |
| <ReactFlow | |
| nodes={nodes} | |
| edges={edges} | |
| nodeTypes={nodeTypes} | |
| onNodeClick={handleNodeClick} | |
| onPaneClick={handlePaneClick} | |
| fitView | |
| fitViewOptions={{ padding: 0.15 }} | |
| minZoom={0.05} | |
| maxZoom={3} | |
| proOptions={{ hideAttribution: true }} | |
| nodesDraggable={false} | |
| nodesConnectable={false} | |
| elementsSelectable | |
| > | |
| <Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#1f2a48" /> | |
| <Controls | |
| showInteractive={false} | |
| style={{ | |
| background: "rgba(17,26,48,0.85)", | |
| border: "1px solid rgba(155,180,255,0.12)", | |
| borderRadius: 8, | |
| }} | |
| /> | |
| <MiniMap | |
| pannable | |
| zoomable | |
| nodeColor={nodeColor} | |
| maskColor="rgba(10,15,28,0.7)" | |
| style={{ | |
| background: "rgba(17,26,48,0.85)", | |
| border: "1px solid rgba(155,180,255,0.12)", | |
| borderRadius: 8, | |
| }} | |
| /> | |
| </ReactFlow> | |
| ); | |
| } | |
| export function GraphView(props: Props) { | |
| return ( | |
| <ReactFlowProvider> | |
| <GraphViewInner {...props} /> | |
| </ReactFlowProvider> | |
| ); | |
| } | |