More compact dagre layout
This commit is contained in:
parent
351605a052
commit
32f55ab086
|
|
@ -137,8 +137,8 @@ const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
|||
|
||||
// Определяем размеры узлов для корректного расположения.
|
||||
// Может потребоваться корректировка в зависимости от реального размера узла в CSS.
|
||||
const nodeWidth = 172;
|
||||
const nodeHeight = 36;
|
||||
const nodeWidth = 120;
|
||||
const nodeHeight = 40;
|
||||
|
||||
/**
|
||||
* Применяет алгоритм расположения Dagre к узлам и рёбрам графа.
|
||||
|
|
@ -148,14 +148,27 @@ const nodeHeight = 36;
|
|||
* @returns {{nodes: any[], edges: any[]}} Объект с обновлёнными узлами и рёбрами, содержащими позиции.
|
||||
*/
|
||||
const getLayoutedElements = (nodes: any[], edges: any[], direction: 'TB' | 'LR' = 'TB') => {
|
||||
const isHorizontal = direction === 'LR'; // Проверяем, горизонтальное ли расположение
|
||||
// ДОБАВЛЕНО: ranksep и nodesep для лучшего отступа между узлами
|
||||
dagreGraph.setGraph({ rankdir: direction, ranksep: 100, nodesep: 50 });
|
||||
const isHorizontal = direction === 'LR';
|
||||
// Компактные настройки для расположения в 2 ряда
|
||||
dagreGraph.setGraph({
|
||||
rankdir: direction,
|
||||
ranksep: 40, // Уменьшенное расстояние между рангами (рядами)
|
||||
nodesep: 10, // Уменьшенное расстояние между узлами в одном ранге
|
||||
edgesep: 20, // Расстояние между рёбрами
|
||||
marginx: 20, // Отступы по X
|
||||
marginy: 20 // Отступы по Y
|
||||
});
|
||||
|
||||
// Добавляем узлы в граф Dagre с их размерами
|
||||
// Добавляем узлы в граф Dagre с динамическими размерами
|
||||
nodes.forEach((node) => {
|
||||
// Убедимся, что узел имеет уникальный id и необходимые размеры для Dagre
|
||||
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
||||
// Пытаемся найти элемент узла в DOM для получения реальных размеров
|
||||
const nodeElement = document.querySelector(`[data-id="${node.id}"]`) as HTMLElement;
|
||||
const dimensions = getNodeDimensions(nodeElement);
|
||||
|
||||
dagreGraph.setNode(node.id, {
|
||||
width: Math.min(dimensions.width + 10, 140), // Ограничиваем максимальную ширину
|
||||
height: Math.min(dimensions.height + 10, 50) // Ограничиваем максимальную высоту
|
||||
});
|
||||
});
|
||||
|
||||
// Добавляем рёбра в граф Dagre
|
||||
|
|
@ -169,22 +182,34 @@ const getLayoutedElements = (nodes: any[], edges: any[], direction: 'TB' | 'LR'
|
|||
|
||||
// Обновляем позиции узлов React Flow на основе расчётов Dagre
|
||||
const newNodes = nodes.map((node) => {
|
||||
const nodeWithPosition: DagreNode = dagreGraph.node(node.id); // Получаем данные узла из Dagre
|
||||
// Смещаем позицию узла Dagre (якорь в центре) к верхнему левому углу,
|
||||
// чтобы она соответствовала точке привязки узла React Flow (верхний левый угол).
|
||||
const nodeWithPosition: DagreNode = dagreGraph.node(node.id);
|
||||
const nodeDimensions = dagreGraph.node(node.id);
|
||||
|
||||
return {
|
||||
...node,
|
||||
// Обновляем targetPosition и sourcePosition в зависимости от направления для корректного отображения ручек
|
||||
targetPosition: isHorizontal ? 'left' : 'top',
|
||||
sourcePosition: isHorizontal ? 'right' : 'bottom',
|
||||
position: {
|
||||
x: nodeWithPosition.x - nodeWidth / 2, // Корректировка X
|
||||
y: nodeWithPosition.y - nodeHeight / 2 // Корректировка Y
|
||||
x: nodeWithPosition.x - nodeDimensions.width / 2,
|
||||
y: nodeWithPosition.y - nodeDimensions.height / 2
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return { nodes: newNodes, edges }; // Возвращаем обновлённые узлы и рёбра
|
||||
return { nodes: newNodes, edges };
|
||||
};
|
||||
|
||||
// Функция для получения реальных размеров узла из DOM
|
||||
const getNodeDimensions = (nodeElement: HTMLElement | null): { width: number; height: number } => {
|
||||
if (!nodeElement) {
|
||||
return { width: 120, height: 40 }; // Fallback размеры
|
||||
}
|
||||
|
||||
const rect = nodeElement.getBoundingClientRect();
|
||||
return {
|
||||
width: Math.max(Math.min(rect.width, 130), 100), // Ограничиваем ширину: мин 100px, макс 130px
|
||||
height: Math.max(Math.min(rect.height, 45), 30) // Ограничиваем высоту: мин 30px, макс 45px
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -214,29 +239,33 @@ export const GraphPanelComponent: React.FC<GraphPanelProps> = ({ graphData, onNo
|
|||
if (graphData.nodes.length > 0 || graphData.edges.length > 0) {
|
||||
console.log('Applying Dagre layout with data:', graphData);
|
||||
|
||||
// Преобразуем входящие данные в формат, необходимый для Dagre и React Flow
|
||||
// Убеждаемся, что каждый узел имеет данные.label для отображения
|
||||
const initialNodesForLayout = graphData.nodes.map((node) => ({
|
||||
id: node.id,
|
||||
// ДОБАВЛЕНО: используем node.type, если он есть, иначе 'default'
|
||||
type: node.type || 'default',
|
||||
data: { label: node.data?.label || `Node ${node.id}` },
|
||||
position: { x: 0, y: 0 } // Временная позиция, будет перезаписана Dagre
|
||||
position: { x: 0, y: 0 }
|
||||
}));
|
||||
|
||||
const initialEdgesForLayout = graphData.edges.map((edge) => ({
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
// Для рёбер можно добавить тип 'default' или ваш кастомный
|
||||
type: edge.type || 'default'
|
||||
}));
|
||||
|
||||
// Применяем Dagre layout
|
||||
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(initialNodesForLayout, initialEdgesForLayout);
|
||||
// Сначала устанавливаем узлы без layout
|
||||
setNodes(initialNodesForLayout);
|
||||
setEdges(initialEdgesForLayout);
|
||||
|
||||
// Обновляем состояния узлов и рёбер React Flow
|
||||
setNodes(layoutedNodes);
|
||||
setEdges(layoutedEdges);
|
||||
// Применяем layout с задержкой, чтобы DOM успел отрендерить узлы
|
||||
setTimeout(() => {
|
||||
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
|
||||
initialNodesForLayout,
|
||||
initialEdgesForLayout
|
||||
);
|
||||
setNodes(layoutedNodes);
|
||||
setEdges(layoutedEdges);
|
||||
}, 100); // Задержка 100ms для рендеринга DOM
|
||||
} else {
|
||||
// Если данных нет, очищаем граф
|
||||
setNodes([]);
|
||||
|
|
|
|||
|
|
@ -178,6 +178,21 @@
|
|||
background-color: var(--llm-assistant-background-color); /* Светло-серый фон */
|
||||
}
|
||||
|
||||
/* Компактные стили для узлов React Flow */
|
||||
.react-flow__node {
|
||||
font-size: 12px !important;
|
||||
padding: 4px 8px !important;
|
||||
min-width: 100px !important;
|
||||
max-width: 130px !important;
|
||||
min-height: 30px !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
|
||||
.react-flow__node-default {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- Chat Panel Styles -------------------------------------------------------- */
|
||||
|
||||
.llm-agent-chat-panel {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user