75 lines
1.8 KiB
TypeScript
75 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import { useLayoutEffect } from 'react';
|
|
import { Edge, MarkerType, Node, ReactFlow, useEdgesState, useNodesState, useReactFlow } from 'reactflow';
|
|
|
|
import { SyntaxTree } from '@/models/rslang';
|
|
|
|
import { ASTEdgeTypes } from './graph/ASTEdgeTypes';
|
|
import { applyLayout } from './graph/ASTLayout';
|
|
import { ASTNodeTypes } from './graph/ASTNodeTypes';
|
|
|
|
interface ASTFlowProps {
|
|
data: SyntaxTree;
|
|
onNodeEnter: (node: Node) => void;
|
|
onNodeLeave: (node: Node) => void;
|
|
}
|
|
|
|
function ASTFlow({ data, onNodeEnter, onNodeLeave }: ASTFlowProps) {
|
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
const [edges, setEdges] = useEdgesState([]);
|
|
const flow = useReactFlow();
|
|
|
|
useLayoutEffect(() => {
|
|
const newNodes = data.map(node => ({
|
|
id: String(node.uid),
|
|
data: node,
|
|
position: { x: 0, y: 0 },
|
|
type: 'token'
|
|
}));
|
|
|
|
const newEdges: Edge[] = [];
|
|
data.forEach(node => {
|
|
if (node.parent !== node.uid) {
|
|
newEdges.push({
|
|
id: String(node.uid),
|
|
source: String(node.parent),
|
|
target: String(node.uid),
|
|
type: 'dynamic',
|
|
focusable: false,
|
|
markerEnd: {
|
|
type: MarkerType.ArrowClosed,
|
|
width: 20,
|
|
height: 20
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
applyLayout(newNodes, newEdges);
|
|
|
|
setNodes(newNodes);
|
|
setEdges(newEdges);
|
|
}, [data, setNodes, setEdges, flow]);
|
|
|
|
return (
|
|
<ReactFlow
|
|
nodes={nodes}
|
|
edges={edges}
|
|
edgesFocusable={false}
|
|
nodesFocusable={false}
|
|
onNodeMouseEnter={(_, node) => onNodeEnter(node)}
|
|
onNodeMouseLeave={(_, node) => onNodeLeave(node)}
|
|
onNodesChange={onNodesChange}
|
|
nodeTypes={ASTNodeTypes}
|
|
edgeTypes={ASTEdgeTypes}
|
|
fitView
|
|
maxZoom={2}
|
|
minZoom={0.5}
|
|
nodesConnectable={false}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export default ASTFlow;
|