From 40d8046617f2ad86c24f96b7599d960ef98b0e1b Mon Sep 17 00:00:00 2001 From: DmitriyA Date: Wed, 23 Apr 2025 08:25:15 -0400 Subject: [PATCH] modified the skeleton MUI --- src/Charts/Components/ChartSkeleton.jsx | 34 +++++ src/Charts/Components/LineChartComponent.jsx | 18 ++- src/Components/hooks/LazyChartBatchRender.jsx | 143 +++++++++++++++--- 3 files changed, 166 insertions(+), 29 deletions(-) create mode 100644 src/Charts/Components/ChartSkeleton.jsx 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