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.
|
// Может потребоваться корректировка в зависимости от реального размера узла в CSS.
|
||||||
const nodeWidth = 172;
|
const nodeWidth = 120;
|
||||||
const nodeHeight = 36;
|
const nodeHeight = 40;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Применяет алгоритм расположения Dagre к узлам и рёбрам графа.
|
* Применяет алгоритм расположения Dagre к узлам и рёбрам графа.
|
||||||
|
|
@ -148,14 +148,27 @@ const nodeHeight = 36;
|
||||||
* @returns {{nodes: any[], edges: any[]}} Объект с обновлёнными узлами и рёбрами, содержащими позиции.
|
* @returns {{nodes: any[], edges: any[]}} Объект с обновлёнными узлами и рёбрами, содержащими позиции.
|
||||||
*/
|
*/
|
||||||
const getLayoutedElements = (nodes: any[], edges: any[], direction: 'TB' | 'LR' = 'TB') => {
|
const getLayoutedElements = (nodes: any[], edges: any[], direction: 'TB' | 'LR' = 'TB') => {
|
||||||
const isHorizontal = direction === 'LR'; // Проверяем, горизонтальное ли расположение
|
const isHorizontal = direction === 'LR';
|
||||||
// ДОБАВЛЕНО: ranksep и nodesep для лучшего отступа между узлами
|
// Компактные настройки для расположения в 2 ряда
|
||||||
dagreGraph.setGraph({ rankdir: direction, ranksep: 100, nodesep: 50 });
|
dagreGraph.setGraph({
|
||||||
|
rankdir: direction,
|
||||||
|
ranksep: 40, // Уменьшенное расстояние между рангами (рядами)
|
||||||
|
nodesep: 10, // Уменьшенное расстояние между узлами в одном ранге
|
||||||
|
edgesep: 20, // Расстояние между рёбрами
|
||||||
|
marginx: 20, // Отступы по X
|
||||||
|
marginy: 20 // Отступы по Y
|
||||||
|
});
|
||||||
|
|
||||||
// Добавляем узлы в граф Dagre с их размерами
|
// Добавляем узлы в граф Dagre с динамическими размерами
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
// Убедимся, что узел имеет уникальный id и необходимые размеры для Dagre
|
// Пытаемся найти элемент узла в DOM для получения реальных размеров
|
||||||
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
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
|
// Добавляем рёбра в граф Dagre
|
||||||
|
|
@ -169,22 +182,34 @@ const getLayoutedElements = (nodes: any[], edges: any[], direction: 'TB' | 'LR'
|
||||||
|
|
||||||
// Обновляем позиции узлов React Flow на основе расчётов Dagre
|
// Обновляем позиции узлов React Flow на основе расчётов Dagre
|
||||||
const newNodes = nodes.map((node) => {
|
const newNodes = nodes.map((node) => {
|
||||||
const nodeWithPosition: DagreNode = dagreGraph.node(node.id); // Получаем данные узла из Dagre
|
const nodeWithPosition: DagreNode = dagreGraph.node(node.id);
|
||||||
// Смещаем позицию узла Dagre (якорь в центре) к верхнему левому углу,
|
const nodeDimensions = dagreGraph.node(node.id);
|
||||||
// чтобы она соответствовала точке привязки узла React Flow (верхний левый угол).
|
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
// Обновляем targetPosition и sourcePosition в зависимости от направления для корректного отображения ручек
|
|
||||||
targetPosition: isHorizontal ? 'left' : 'top',
|
targetPosition: isHorizontal ? 'left' : 'top',
|
||||||
sourcePosition: isHorizontal ? 'right' : 'bottom',
|
sourcePosition: isHorizontal ? 'right' : 'bottom',
|
||||||
position: {
|
position: {
|
||||||
x: nodeWithPosition.x - nodeWidth / 2, // Корректировка X
|
x: nodeWithPosition.x - nodeDimensions.width / 2,
|
||||||
y: nodeWithPosition.y - nodeHeight / 2 // Корректировка Y
|
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) {
|
if (graphData.nodes.length > 0 || graphData.edges.length > 0) {
|
||||||
console.log('Applying Dagre layout with data:', graphData);
|
console.log('Applying Dagre layout with data:', graphData);
|
||||||
|
|
||||||
// Преобразуем входящие данные в формат, необходимый для Dagre и React Flow
|
|
||||||
// Убеждаемся, что каждый узел имеет данные.label для отображения
|
|
||||||
const initialNodesForLayout = graphData.nodes.map((node) => ({
|
const initialNodesForLayout = graphData.nodes.map((node) => ({
|
||||||
id: node.id,
|
id: node.id,
|
||||||
// ДОБАВЛЕНО: используем node.type, если он есть, иначе 'default'
|
|
||||||
type: node.type || 'default',
|
type: node.type || 'default',
|
||||||
data: { label: node.data?.label || `Node ${node.id}` },
|
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) => ({
|
const initialEdgesForLayout = graphData.edges.map((edge) => ({
|
||||||
id: edge.id,
|
id: edge.id,
|
||||||
source: edge.source,
|
source: edge.source,
|
||||||
target: edge.target,
|
target: edge.target,
|
||||||
// Для рёбер можно добавить тип 'default' или ваш кастомный
|
|
||||||
type: edge.type || 'default'
|
type: edge.type || 'default'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Применяем Dagre layout
|
// Сначала устанавливаем узлы без layout
|
||||||
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(initialNodesForLayout, initialEdgesForLayout);
|
setNodes(initialNodesForLayout);
|
||||||
|
setEdges(initialEdgesForLayout);
|
||||||
|
|
||||||
// Обновляем состояния узлов и рёбер React Flow
|
// Применяем layout с задержкой, чтобы DOM успел отрендерить узлы
|
||||||
|
setTimeout(() => {
|
||||||
|
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
|
||||||
|
initialNodesForLayout,
|
||||||
|
initialEdgesForLayout
|
||||||
|
);
|
||||||
setNodes(layoutedNodes);
|
setNodes(layoutedNodes);
|
||||||
setEdges(layoutedEdges);
|
setEdges(layoutedEdges);
|
||||||
|
}, 100); // Задержка 100ms для рендеринга DOM
|
||||||
} else {
|
} else {
|
||||||
// Если данных нет, очищаем граф
|
// Если данных нет, очищаем граф
|
||||||
setNodes([]);
|
setNodes([]);
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,21 @@
|
||||||
background-color: var(--llm-assistant-background-color); /* Светло-серый фон */
|
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 -------------------------------------------------------- */
|
/* ----------------------------------------------- Chat Panel Styles -------------------------------------------------------- */
|
||||||
|
|
||||||
.llm-agent-chat-panel {
|
.llm-agent-chat-panel {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user