fixed a bug with tabs
parent
40d8046617
commit
6fd5d1aed2
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
List,
|
List,
|
||||||
|
|
@ -35,11 +35,10 @@ const SidebarResizer = styled('div')(({ theme }) => ({
|
||||||
const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed, setCollapsed }) => {
|
const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed, setCollapsed }) => {
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const [menuData, setMenuData] = useState(data);
|
const [menuData, setMenuData] = useState(data);
|
||||||
|
const contentCache = useRef({});
|
||||||
|
|
||||||
// Обновляем статусы при изменении данных
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
// Создаем глубокую копию данных, чтобы не мутировать исходные
|
|
||||||
const dataCopy = JSON.parse(JSON.stringify(data));
|
const dataCopy = JSON.parse(JSON.stringify(data));
|
||||||
statusManager1.updateStatuses(dataCopy);
|
statusManager1.updateStatuses(dataCopy);
|
||||||
setMenuData(dataCopy);
|
setMenuData(dataCopy);
|
||||||
|
|
@ -51,9 +50,16 @@ const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed,
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectItem = (id, title, children) => {
|
const handleSelectItem = (id, title, children) => {
|
||||||
onOpenTab(id, title, children);
|
onOpenTab(id, title);
|
||||||
|
|
||||||
|
contentCache.current = tabContent({ items: children }, contentCache.current);
|
||||||
|
if (contentCache.current[id]) {
|
||||||
|
onOpenTab(id, title, contentCache.current[id].content);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const drawerWidth = collapsed ? 64 : sidebarWidth;
|
const drawerWidth = collapsed ? 64 : sidebarWidth;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,23 @@ import Box from '@mui/material/Box';
|
||||||
const PrometheusChart = lazy(() => import('../../Charts/PrometheusChart'));
|
const PrometheusChart = lazy(() => import('../../Charts/PrometheusChart'));
|
||||||
import LazyChartBatchRenderer from "../hooks/LazyChartBatchRender";
|
import LazyChartBatchRenderer from "../hooks/LazyChartBatchRender";
|
||||||
|
|
||||||
// Функция для генерации названия метрики на основе id
|
|
||||||
const getMetricName = (id) => {
|
const getMetricName = (id) => {
|
||||||
return `zvks_apiforsnmp_measure_${id}`;
|
return `zvks_apiforsnmp_measure_${id}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Функция для рекурсивного сбора всех id потомков
|
|
||||||
const getAllChildIds = (node) => {
|
const getAllChildIds = (node) => {
|
||||||
let ids = [];
|
let ids = [];
|
||||||
if (node.id) {
|
if (node.id) {
|
||||||
ids.push(node.id); // Добавляем id текущего узла
|
ids.push(node.id);
|
||||||
}
|
}
|
||||||
if (node.items && node.items.length > 0) {
|
if (node.items && node.items.length > 0) {
|
||||||
node.items.forEach((child) => {
|
node.items.forEach((child) => {
|
||||||
ids = ids.concat(getAllChildIds(child)); // Рекурсивно собираем id потомков
|
ids = ids.concat(getAllChildIds(child));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Компонент Skeleton для графика
|
|
||||||
const ChartSkeleton = () => (
|
const ChartSkeleton = () => (
|
||||||
<Box sx={{ width: '100%' }}>
|
<Box sx={{ width: '100%' }}>
|
||||||
<Skeleton variant="text" width="60%" height={30} /> {/* Заголовок */}
|
<Skeleton variant="text" width="60%" height={30} /> {/* Заголовок */}
|
||||||
|
|
@ -32,7 +29,6 @@ const ChartSkeleton = () => (
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Компонент Skeleton для родительского контейнера
|
|
||||||
const ContainerSkeleton = () => (
|
const ContainerSkeleton = () => (
|
||||||
<Box sx={{ width: '100%' }}>
|
<Box sx={{ width: '100%' }}>
|
||||||
<Skeleton variant="text" width="40%" height={40} /> {/* Заголовок */}
|
<Skeleton variant="text" width="40%" height={40} /> {/* Заголовок */}
|
||||||
|
|
@ -46,69 +42,52 @@ const ContainerSkeleton = () => (
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
const tabContent = (data) => {
|
const tabContent = (data, existingContent = {}) => {
|
||||||
const tabContent = {};
|
const tabContent = { ...existingContent };
|
||||||
|
|
||||||
// Функция для рекурсивного обхода и сбора данных
|
|
||||||
// Функция для рекурсивного обхода и сбора данных
|
|
||||||
const generateContent = (nodes) => {
|
const generateContent = (nodes) => {
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
// Если у узла есть вложенные элементы, рекурсивно обрабатываем их
|
if (tabContent[node.id]) return;
|
||||||
|
|
||||||
if (node.items && node.items.length > 0) {
|
if (node.items && node.items.length > 0) {
|
||||||
// Создаем контент для родителя
|
generateContent(node.items);
|
||||||
const childrenContent = generateContent(node.items);
|
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<div>
|
<div key={node.id}>
|
||||||
<h2>{node.title}</h2>
|
<h2>{node.title}</h2>
|
||||||
<Suspense fallback={<ContainerSkeleton />}>
|
<Suspense fallback={<ContainerSkeleton />}>
|
||||||
<LazyChartBatchRenderer charts={node.items.map((child) => tabContent[child.id].content)} />
|
<LazyChartBatchRenderer
|
||||||
|
charts={node.items.map(child => tabContent[child.id]?.content) || <ChartSkeleton />}
|
||||||
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<p>Контент для {node.title}.</p>
|
|
||||||
{/*childrenContent*/}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Сохраняем контент для текущего id
|
|
||||||
tabContent[node.id] = {
|
tabContent[node.id] = {
|
||||||
title: node.title,
|
title: node.title,
|
||||||
content: content,
|
content: content,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Если у узла нет вложенных элементов, это самый нижний уровень
|
|
||||||
const metricName = getMetricName(node.id);
|
const metricName = getMetricName(node.id);
|
||||||
const content = (
|
const content = (
|
||||||
<div key={node.id}>
|
<div key={node.id}>
|
||||||
<h3>{node.title}</h3> {/* Используем title узла */}
|
<h3>{node.title}</h3>
|
||||||
<Suspense fallback={<ChartSkeleton />}>
|
<Suspense fallback={<ChartSkeleton />}>
|
||||||
<PrometheusChart metricName={metricName} />
|
<PrometheusChart metricName={metricName} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Сохраняем контент для текущего id
|
|
||||||
tabContent[node.id] = {
|
tabContent[node.id] = {
|
||||||
title: node.title,
|
title: node.title,
|
||||||
content: content,
|
content: content,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Возвращаем контент для всех потомков
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{nodes.map((node) => (
|
|
||||||
<div key={node.id}>{tabContent[node.id].content}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Начинаем обработку с корневого уровня
|
|
||||||
if (data.items && data.items.length > 0) {
|
if (data.items && data.items.length > 0) {
|
||||||
generateContent(data.items);
|
generateContent(data.items);
|
||||||
} else {
|
|
||||||
console.warn("Данные отсутствуют или массив items пуст");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tabContent;
|
return tabContent;
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,63 @@ const useTabs = (initialTab) => {
|
||||||
const [tabs, setTabs] = useState([]);
|
const [tabs, setTabs] = useState([]);
|
||||||
const [activeTab, setActiveTab] = useState(initialTab);
|
const [activeTab, setActiveTab] = useState(initialTab);
|
||||||
|
|
||||||
const handleOpenTab = useCallback((id, title) => {
|
const handleOpenTab = useCallback((id, title, content) => {
|
||||||
setTabs((prevTabs) =>
|
setTabs((prevTabs) => {
|
||||||
prevTabs.some((tab) => tab.id === id)
|
const existingTabIndex = prevTabs.findIndex(tab => tab.id === id);
|
||||||
? prevTabs
|
|
||||||
: [...prevTabs, { id, title }]
|
if (existingTabIndex >= 0) {
|
||||||
);
|
return prevTabs.map((tab, index) => ({
|
||||||
|
...tab,
|
||||||
|
content: content || tab.content,
|
||||||
|
active: index === existingTabIndex
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем новую вкладку
|
||||||
|
return [
|
||||||
|
...prevTabs.map(tab => ({ ...tab, active: false })),
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
content: content || <div>Loading...</div>,
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
setActiveTab(id);
|
setActiveTab(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCloseTab = useCallback((id) => {
|
const handleCloseTab = useCallback((id) => {
|
||||||
setTabs((prevTabs) => prevTabs.filter((tab) => tab.id !== id));
|
setTabs((prevTabs) => {
|
||||||
if (activeTab === id) {
|
const newTabs = prevTabs.filter((tab) => tab.id !== id);
|
||||||
setActiveTab(tabs.length > 1 ? tabs[tabs.length - 2].id : initialTab);
|
|
||||||
}
|
|
||||||
}, [activeTab, tabs, initialTab]);
|
|
||||||
|
|
||||||
return { tabs, activeTab, handleOpenTab, handleCloseTab, setActiveTab };
|
if (activeTab === id) {
|
||||||
|
setActiveTab(newTabs.length > 0
|
||||||
|
? newTabs[newTabs.length - 1].id
|
||||||
|
: initialTab
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newTabs;
|
||||||
|
});
|
||||||
|
}, [activeTab, initialTab]);
|
||||||
|
|
||||||
|
const updateTabContent = useCallback((id, content) => {
|
||||||
|
setTabs(prevTabs =>
|
||||||
|
prevTabs.map(tab =>
|
||||||
|
tab.id === id ? { ...tab, content } : tab
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tabs,
|
||||||
|
activeTab,
|
||||||
|
handleOpenTab,
|
||||||
|
handleCloseTab,
|
||||||
|
setActiveTab,
|
||||||
|
updateTabContent
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useTabs;
|
export default useTabs;
|
||||||
Loading…
Reference in New Issue