diff --git a/src/Charts/Components/ChartSkeleton.jsx b/src/Charts/Components/ChartSkeleton.jsx
new file mode 100644
index 0000000..7500023
--- /dev/null
+++ b/src/Charts/Components/ChartSkeleton.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Box, Skeleton } from '@mui/material';
+
+const ChartSkeleton = ({ count = 1 }) => {
+ return (
+ <>
+ {Array.from({ length: count }).map((_, index) => (
+
+
+
+
+
+
+
+ {[1, 2, 3, 4].map((i) => (
+
+ ))}
+
+
+ ))}
+ >
+ );
+};
+
+export default ChartSkeleton;
\ No newline at end of file
diff --git a/src/Charts/Components/LineChartComponent.jsx b/src/Charts/Components/LineChartComponent.jsx
index c48d46f..149f1fb 100755
--- a/src/Charts/Components/LineChartComponent.jsx
+++ b/src/Charts/Components/LineChartComponent.jsx
@@ -1,6 +1,8 @@
import React, { useState, useRef, useEffect } from 'react';
import { LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Line, ResponsiveContainer, ReferenceArea } from 'recharts';
+import { Skeleton } from '@mui/material';
import { HOUR, DAY } from './constants';
+
const TIME_FORMATS = {
LONG: 'dd.MM HH:mm', // Для диапазона > 24 часов
MEDIUM: 'HH:mm', // Для диапазона > 1 часа
@@ -51,7 +53,6 @@ const LineChartComponent = ({
? Object.keys(displayData[0]).filter(k => !['timestamp', 'time', 'fullTime'].includes(k))
: [];
- // Функция для определения оптимального формата времени в зависимости от диапазона
const getTimeFormat = () => {
if (!data.length) return TIME_FORMATS.SHORT;
@@ -77,7 +78,6 @@ const LineChartComponent = ({
const handleMouseDown = (e) => {
if (!e) return;
- // Получаем индекс точки по координатам
const activeIndex = e.activeTooltipIndex;
if (activeIndex === undefined || activeIndex < 0 || activeIndex >= data.length) return;
@@ -113,7 +113,6 @@ const LineChartComponent = ({
const startIndex = Math.min(selectionArea.startIndex, selectionArea.endIndex);
const endIndex = Math.max(selectionArea.startIndex, selectionArea.endIndex);
- // Нормализуем индексы к диапазону [0, 1] для родительского компонента
const normalizedStart = startIndex / (data.length - 1);
const normalizedEnd = endIndex / (data.length - 1);
@@ -152,7 +151,18 @@ const LineChartComponent = ({
};
if (!data.length) {
- return
Нет данных для отображения
;
+ return (
+
+
+
+
+ );
}
return (
diff --git a/src/Components/hooks/LazyChartBatchRender.jsx b/src/Components/hooks/LazyChartBatchRender.jsx
index d9f1dca..ed351e7 100644
--- a/src/Components/hooks/LazyChartBatchRender.jsx
+++ b/src/Components/hooks/LazyChartBatchRender.jsx
@@ -1,13 +1,23 @@
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState, useCallback } from 'react';
import Box from '@mui/material/Box';
import Skeleton from '@mui/material/Skeleton';
const LazyChartBatchRenderer = ({ charts }) => {
const [visibleIndices, setVisibleIndices] = useState(new Set());
const placeholderRefs = useRef([]);
+ const observerRef = useRef(null);
+ const cleanupTimeoutRef = useRef(null);
const ChartSkeleton = () => (
-
+
@@ -15,46 +25,124 @@ const LazyChartBatchRenderer = ({ charts }) => {
-
+
{[1, 2, 3, 4].map((_, i) => (
-
+
))}
);
+
+ const isElementFarFromViewport = useCallback((element) => {
+ if (!element) return true;
+
+ const rect = element.getBoundingClientRect();
+ const buffer = window.innerHeight * 1.5;
+
+
+ return rect.bottom < -buffer || rect.top > window.innerHeight + buffer;
+ }, []);
+
+
+ const updateVisibleIndices = useCallback(() => {
+ const newVisibleIndices = new Set();
+
+ placeholderRefs.current.forEach((ref, index) => {
+ if (ref && !isElementFarFromViewport(ref)) {
+ newVisibleIndices.add(index);
+ }
+ });
+
+ setVisibleIndices(prev => {
+ if (newVisibleIndices.size === prev.size &&
+ Array.from(newVisibleIndices).every(i => prev.has(i))) {
+ return prev;
+ }
+ return newVisibleIndices;
+ });
+ }, [isElementFarFromViewport]);
+
useEffect(() => {
- const observer = new IntersectionObserver(
+ observerRef.current = new IntersectionObserver(
(entries) => {
- setVisibleIndices((prev) => {
- const updated = new Set(prev);
- entries.forEach((entry) => {
- const index = parseInt(entry.target.dataset.index, 10);
- if (entry.isIntersecting) {
- updated.add(index);
- } else {
- updated.delete(index);
- }
- });
- return updated;
+ entries.forEach(entry => {
+ if (entry.isIntersecting) {
+ updateVisibleIndices();
+ }
});
},
{
root: null,
- rootMargin: '200px',
- threshold: 0.1,
+ rootMargin: '500px 0px',
+ threshold: 0.01
}
);
- placeholderRefs.current.forEach((ref) => {
- if (ref) observer.observe(ref);
+ placeholderRefs.current.forEach(ref => {
+ if (ref) observerRef.current.observe(ref);
});
- return () => {
- observer.disconnect();
+ const handleScroll = () => {
+ if (cleanupTimeoutRef.current) {
+ clearTimeout(cleanupTimeoutRef.current);
+ }
+
+ cleanupTimeoutRef.current = setTimeout(() => {
+ updateVisibleIndices();
+
+ setVisibleIndices(prev => {
+ const updated = new Set(prev);
+ let changed = false;
+
+ placeholderRefs.current.forEach((ref, index) => {
+ if (ref && isElementFarFromViewport(ref) && prev.has(index)) {
+ updated.delete(index);
+ changed = true;
+ }
+ });
+
+ return changed ? updated : prev;
+ });
+ }, 150);
};
- }, [charts]);
+
+ window.addEventListener('scroll', handleScroll, { passive: true });
+ window.addEventListener('resize', updateVisibleIndices, { passive: true });
+
+ updateVisibleIndices();
+
+ return () => {
+ if (cleanupTimeoutRef.current) clearTimeout(cleanupTimeoutRef.current);
+ if (observerRef.current) observerRef.current.disconnect();
+ window.removeEventListener('scroll', handleScroll);
+ window.removeEventListener('resize', updateVisibleIndices);
+ };
+ }, [updateVisibleIndices]);
+
+ const shouldShowChart = (index) => {
+ return visibleIndices.has(index) ||
+ visibleIndices.has(index - 1) ||
+ visibleIndices.has(index + 1);
+ };
return (
@@ -63,12 +151,17 @@ const LazyChartBatchRenderer = ({ charts }) => {
key={index}
ref={(el) => (placeholderRefs.current[index] = el)}
data-index={index}
+ style={{
+ minHeight: '400px',
+ marginBottom: '20px',
+ transition: 'opacity 0.3s ease',
+ }}
>
- {visibleIndices.has(index) ? chart : }
+ {shouldShowChart(index) ? chart : }
))}
);
};
-export default LazyChartBatchRenderer;
+export default React.memo(LazyChartBatchRenderer);
\ No newline at end of file