|
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(() => { |
|
|
|
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; |
|
|