Spaces:
Sleeping
Sleeping
File size: 2,597 Bytes
fc01079 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 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>
);
}
|