mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
feat(card): add visibility handling and loading placeholders for FormatCard and RegexCard components to improve performance
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, {useState} from 'react';
|
import React, {useState, useEffect, useRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {Copy, Check, FlaskConical, FileText, ListFilter} from 'lucide-react';
|
import {Copy, Check, FlaskConical, FileText, ListFilter} from 'lucide-react';
|
||||||
import Tooltip from '@ui/Tooltip';
|
import Tooltip from '@ui/Tooltip';
|
||||||
@@ -14,6 +14,8 @@ function FormatCard({
|
|||||||
willBeSelected,
|
willBeSelected,
|
||||||
onSelect
|
onSelect
|
||||||
}) {
|
}) {
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
const cardRef = useRef(null);
|
||||||
const [showDescription, setShowDescription] = useState(() => {
|
const [showDescription, setShowDescription] = useState(() => {
|
||||||
const saved = localStorage.getItem(`format-view-${format.file_name}`);
|
const saved = localStorage.getItem(`format-view-${format.file_name}`);
|
||||||
return saved !== null ? JSON.parse(saved) : true;
|
return saved !== null ? JSON.parse(saved) : true;
|
||||||
@@ -64,8 +66,27 @@ function FormatCard({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
setIsVisible(entry.isIntersecting);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0,
|
||||||
|
rootMargin: '100px' // Keep cards rendered 100px outside viewport
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cardRef.current) {
|
||||||
|
observer.observe(cardRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={cardRef}
|
||||||
className={`w-full h-[12rem] bg-gradient-to-br from-gray-800/95 to-gray-900 border ${
|
className={`w-full h-[12rem] bg-gradient-to-br from-gray-800/95 to-gray-900 border ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-blue-500'
|
? 'border-blue-500'
|
||||||
@@ -81,7 +102,8 @@ function FormatCard({
|
|||||||
} transition-all cursor-pointer relative`}
|
} transition-all cursor-pointer relative`}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
onMouseDown={handleMouseDown}>
|
onMouseDown={handleMouseDown}>
|
||||||
<div className='p-4 flex flex-col h-full'>
|
{isVisible ? (
|
||||||
|
<div className='p-4 flex flex-col h-full'>
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className='flex justify-between items-start'>
|
<div className='flex justify-between items-start'>
|
||||||
<div className='flex flex-col min-w-0 flex-1'>
|
<div className='flex flex-col min-w-0 flex-1'>
|
||||||
@@ -237,6 +259,15 @@ function FormatCard({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className='p-4 flex items-center justify-center h-full'>
|
||||||
|
<div className='w-full space-y-2'>
|
||||||
|
<div className='h-5 bg-gray-700/50 rounded animate-pulse'/>
|
||||||
|
<div className='h-3 bg-gray-700/50 rounded animate-pulse w-3/4'/>
|
||||||
|
<div className='h-3 bg-gray-700/50 rounded animate-pulse w-1/2'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, {useState, useEffect, useRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {Copy, Check, FlaskConical} from 'lucide-react';
|
import {Copy, Check, FlaskConical} from 'lucide-react';
|
||||||
import Tooltip from '@ui/Tooltip';
|
import Tooltip from '@ui/Tooltip';
|
||||||
@@ -15,6 +15,9 @@ const RegexCard = ({
|
|||||||
willBeSelected,
|
willBeSelected,
|
||||||
onSelect
|
onSelect
|
||||||
}) => {
|
}) => {
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
const cardRef = useRef(null);
|
||||||
|
|
||||||
const totalTests = pattern.tests?.length || 0;
|
const totalTests = pattern.tests?.length || 0;
|
||||||
const passedTests = pattern.tests?.filter(t => t.passes)?.length || 0;
|
const passedTests = pattern.tests?.filter(t => t.passes)?.length || 0;
|
||||||
const passRate =
|
const passRate =
|
||||||
@@ -46,8 +49,27 @@ const RegexCard = ({
|
|||||||
return 'text-red-400';
|
return 'text-red-400';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
setIsVisible(entry.isIntersecting);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0,
|
||||||
|
rootMargin: '100px' // Keep cards rendered 100px outside viewport
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cardRef.current) {
|
||||||
|
observer.observe(cardRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={cardRef}
|
||||||
className={`w-full h-[20rem] bg-gradient-to-br from-gray-800/95 to-gray-900 border ${
|
className={`w-full h-[20rem] bg-gradient-to-br from-gray-800/95 to-gray-900 border ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-blue-500'
|
? 'border-blue-500'
|
||||||
@@ -63,7 +85,8 @@ const RegexCard = ({
|
|||||||
} transition-all cursor-pointer overflow-hidden`}
|
} transition-all cursor-pointer overflow-hidden`}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
onMouseDown={handleMouseDown}>
|
onMouseDown={handleMouseDown}>
|
||||||
<div className='p-6 flex flex-col h-full'>
|
{isVisible ? (
|
||||||
|
<div className='p-6 flex flex-col h-full'>
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className='flex-none'>
|
<div className='flex-none'>
|
||||||
<div className='flex justify-between items-start'>
|
<div className='flex justify-between items-start'>
|
||||||
@@ -183,6 +206,15 @@ const RegexCard = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className='p-6 flex items-center justify-center h-full'>
|
||||||
|
<div className='w-full space-y-3'>
|
||||||
|
<div className='h-6 bg-gray-700/50 rounded animate-pulse'/>
|
||||||
|
<div className='h-20 bg-gray-700/50 rounded animate-pulse'/>
|
||||||
|
<div className='h-4 bg-gray-700/50 rounded animate-pulse w-3/4'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user