Files
HellreigN/frontend/src/modules/graph/Graph.tsx
T
nikita 55cb214458
ci-front / build (push) Successful in 2m17s
feat: themes
2026-04-04 13:38:32 +03:00

112 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useRef, useEffect, useState } from "react";
import type {
GraphData,
GraphNode,
GraphLink,
ContextMenuState,
} from "./types";
import { useGraphStore } from "./store/useGraphStore";
import {
ForceGraph,
GraphControls,
GraphContextMenu,
GraphStatusBar,
GraphStats,
} from "./components";
interface GraphProps {
initialData?: GraphData;
onExport?: () => void;
onDataChange?: (data: GraphData) => void;
}
export const Graph: React.FC<GraphProps> = ({
initialData,
onExport,
onDataChange,
}) => {
const fgRef = useRef<any>(null);
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
const data = useGraphStore((s) => s.data);
const isLinkMode = useGraphStore((s) => s.isLinkMode);
const selectedNode = useGraphStore((s) => s.selectedNode);
const setData = useGraphStore((s) => s.setData);
// Инициализация данных
useEffect(() => {
if (initialData) setData(initialData);
}, [initialData, setData]);
// Закрыть контекстное меню по клику вне
useEffect(() => {
const handleClickOutside = () => setContextMenu(null);
document.addEventListener("click", handleClickOutside);
return () => document.removeEventListener("click", handleClickOutside);
}, []);
const handleNodeRightClick = (node: GraphNode, event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
setContextMenu({ x: event.clientX, y: event.clientY, node, link: null });
};
const handleLinkRightClick = (link: GraphLink, event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
setContextMenu({ x: event.clientX, y: event.clientY, node: null, link });
};
if (!data || data.nodes.length === 0) {
return (
<div className="bg-gray-900 rounded-xl shadow-lg p-6">
<div className="flex items-center justify-center h-96">
<div className="text-center">
<p className="text-gray-400 mb-4">Нет данных для отображения</p>
</div>
</div>
</div>
);
}
return (
<div
className="p-4 h-full flex flex-col"
style={{ backgroundColor: "var(--card-bg)" }}
>
{/* Статистика сверху */}
<GraphStats data={data} />
{/* Граф */}
<div
className="flex-1 rounded-lg overflow-hidden relative mt-2"
style={{ border: "1px solid var(--border)" }}
>
<ForceGraph
ref={fgRef}
data={data}
onNodeRightClick={handleNodeRightClick}
onLinkRightClick={handleLinkRightClick}
/>
<GraphContextMenu
menu={contextMenu}
data={data}
onClose={() => setContextMenu(null)}
/>
<GraphStatusBar isLinkMode={isLinkMode} selectedNode={selectedNode} />
</div>
{/* Кнопки снизу */}
<GraphControls
fgRef={fgRef}
onExport={onExport}
onDataChange={onDataChange}
/>
</div>
);
};
export default Graph;