hf-model-viewer / src /components /GraphView.tsx
tfrere's picture
tfrere HF Staff
Deploy hf-model-viewer 2026-05-22T16:59:58Z
fc01079 verified
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>
);
}