94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
import React, { useRef, useEffect, useState, ReactNode } from 'react';
|
|
import { sampleImageModalState } from '@/components/SampleImageModal';
|
|
import { isVideo } from '@/utils/basic';
|
|
|
|
interface SampleImageCardProps {
|
|
imageUrl: string;
|
|
alt: string;
|
|
numSamples: number;
|
|
sampleImages: string[];
|
|
children?: ReactNode;
|
|
className?: string;
|
|
onDelete?: () => void;
|
|
}
|
|
|
|
const SampleImageCard: React.FC<SampleImageCardProps> = ({
|
|
imageUrl,
|
|
alt,
|
|
numSamples,
|
|
sampleImages,
|
|
children,
|
|
className = '',
|
|
}) => {
|
|
const cardRef = useRef<HTMLDivElement>(null);
|
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
|
const [loaded, setLoaded] = useState<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
// Create intersection observer to check visibility
|
|
const observer = new IntersectionObserver(
|
|
entries => {
|
|
if (entries[0].isIntersecting) {
|
|
setIsVisible(true);
|
|
observer.disconnect();
|
|
}
|
|
},
|
|
{ threshold: 0.1 },
|
|
);
|
|
|
|
if (cardRef.current) {
|
|
observer.observe(cardRef.current);
|
|
}
|
|
|
|
return () => {
|
|
observer.disconnect();
|
|
};
|
|
}, []);
|
|
|
|
const handleLoad = (): void => {
|
|
setLoaded(true);
|
|
};
|
|
console.log('imgurl',imageUrl.toLowerCase().slice(-4))
|
|
|
|
return (
|
|
<div className={`flex flex-col ${className}`}>
|
|
{/* Square image container */}
|
|
<div
|
|
ref={cardRef}
|
|
className="relative w-full cursor-pointer"
|
|
style={{ paddingBottom: '100%' }} // Make it square
|
|
onClick={() => sampleImageModalState.set({ imgPath: imageUrl, numSamples, sampleImages })}
|
|
>
|
|
<div className="absolute inset-0 rounded-t-lg shadow-md">
|
|
{isVisible && (
|
|
<>
|
|
{isVideo(imageUrl) ? (
|
|
<video
|
|
src={`/api/img/${encodeURIComponent(imageUrl)}`}
|
|
className={`w-full h-full object-cover`}
|
|
autoPlay={false}
|
|
loop
|
|
muted
|
|
playsInline
|
|
/>
|
|
) : (
|
|
<img
|
|
src={`/api/img/${encodeURIComponent(imageUrl)}`}
|
|
alt={alt}
|
|
onLoad={handleLoad}
|
|
className={`w-full h-full object-cover transition-opacity duration-300 ${
|
|
loaded ? 'opacity-100' : 'opacity-0'
|
|
}`}
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
{children && <div className="absolute inset-0 flex items-center justify-center">{children}</div>}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SampleImageCard;
|