2023-07-30 00:47:07 +03:00
|
|
|
import { useMemo, useRef } from 'react';
|
|
|
|
import Select from 'react-select';
|
|
|
|
import { GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, useSelection } from 'reagraph';
|
2023-07-29 23:00:03 +03:00
|
|
|
|
2023-07-30 00:47:07 +03:00
|
|
|
import GraphThemed from '../../components/Common/GraphThemed';
|
2023-07-29 21:23:18 +03:00
|
|
|
import { useRSForm } from '../../context/RSFormContext';
|
2023-07-30 00:47:07 +03:00
|
|
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
|
|
|
import { GraphLayoutSelector } from '../../utils/staticUI';
|
2023-07-29 21:23:18 +03:00
|
|
|
|
|
|
|
function EditorTermGraph() {
|
|
|
|
const { schema } = useRSForm();
|
2023-07-30 00:47:07 +03:00
|
|
|
const [ layout, setLayout ] = useLocalStorage<LayoutTypes>('graph_layout', 'forceatlas2');
|
|
|
|
const graphRef = useRef<GraphCanvasRef | null>(null);
|
2023-07-29 23:00:03 +03:00
|
|
|
|
|
|
|
const nodes: GraphNode[] = useMemo(() => {
|
|
|
|
return schema?.items.map(cst => {
|
|
|
|
return {
|
|
|
|
id: String(cst.id),
|
|
|
|
label: (cst.term.resolved || cst.term.raw) ? `${cst.alias}: ${cst.term.resolved || cst.term.raw}` : cst.alias
|
|
|
|
}}
|
|
|
|
) ?? [];
|
|
|
|
}, [schema?.items]);
|
2023-07-29 21:23:18 +03:00
|
|
|
|
2023-07-30 00:47:07 +03:00
|
|
|
const edges: GraphEdge[] = useMemo(() => {
|
2023-07-29 23:00:03 +03:00
|
|
|
const result: GraphEdge[] = [];
|
|
|
|
let edgeID = 1;
|
|
|
|
schema?.graph.nodes.forEach(source => {
|
|
|
|
source.adjacent.forEach(target => {
|
|
|
|
result.push({
|
|
|
|
id: String(edgeID),
|
|
|
|
source: String(source.id),
|
|
|
|
target: String(target)
|
|
|
|
});
|
|
|
|
edgeID += 1;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}, [schema?.graph]);
|
2023-07-29 21:23:18 +03:00
|
|
|
|
2023-07-30 00:47:07 +03:00
|
|
|
const {
|
|
|
|
selections, actives,
|
|
|
|
onNodeClick,
|
|
|
|
onCanvasClick,
|
|
|
|
onNodePointerOver,
|
|
|
|
onNodePointerOut
|
|
|
|
} = useSelection({
|
|
|
|
type: 'multi', // 'single' | 'multi' | 'multiModifier'
|
|
|
|
ref: graphRef,
|
|
|
|
nodes,
|
|
|
|
edges,
|
|
|
|
pathSelectionType: 'all',
|
|
|
|
focusOnSelect: 'singleOnly'
|
|
|
|
});
|
|
|
|
|
2023-07-29 21:23:18 +03:00
|
|
|
return (
|
2023-07-30 00:47:07 +03:00
|
|
|
<div>
|
|
|
|
<div className='relative w-full'>
|
|
|
|
<div className='absolute top-0 left-0 z-20 px-3 py-2'>
|
|
|
|
<Select
|
|
|
|
className='w-[10rem]'
|
|
|
|
options={GraphLayoutSelector}
|
|
|
|
placeholder='Выберите тип'
|
|
|
|
value={layout && { value: layout, label: String(layout) }}
|
|
|
|
onChange={data => { data && setLayout(data.value); }}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<GraphThemed ref={graphRef}
|
|
|
|
sizeClass='w-[1240px] h-[800px] 2xl:w-[1880px] 2xl:h-[800px]'
|
|
|
|
nodes={nodes}
|
|
|
|
edges={edges}
|
|
|
|
draggable
|
|
|
|
layoutType={layout}
|
|
|
|
selections={selections}
|
|
|
|
actives={actives}
|
|
|
|
onCanvasClick={onCanvasClick}
|
|
|
|
onNodeClick={onNodeClick}
|
|
|
|
onNodePointerOver={onNodePointerOver}
|
|
|
|
onNodePointerOut={onNodePointerOut}
|
|
|
|
// sizingType="default" // 'none' | 'pagerank' | 'centrality' | 'attribute' | 'default';
|
|
|
|
cameraMode={ layout.includes('3d') ? 'rotate' : 'pan'}
|
2023-07-29 23:00:03 +03:00
|
|
|
/>
|
2023-07-29 21:23:18 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-07-29 23:00:03 +03:00
|
|
|
|
2023-07-29 21:23:18 +03:00
|
|
|
export default EditorTermGraph;
|