import { Skeleton } from '@/components/ui/skeleton' import { cn } from '@/lib/utils' import { TImageInfo } from '@/types' import { decode } from 'blurhash' import { ImageOff } from 'lucide-react' import { HTMLAttributes, useEffect, useState } from 'react' export default function Image({ image: { url, blurHash }, alt, className = '', classNames = {}, hideIfError = false, ...props }: HTMLAttributes & { classNames?: { wrapper?: string errorPlaceholder?: string } image: TImageInfo alt?: string hideIfError?: boolean }) { const [isLoading, setIsLoading] = useState(true) const [displayBlurHash, setDisplayBlurHash] = useState(true) const [blurDataUrl, setBlurDataUrl] = useState(null) const [hasError, setHasError] = useState(false) useEffect(() => { if (blurHash) { const { numX, numY } = decodeBlurHashSize(blurHash) const width = numX * 3 const height = numY * 3 const pixels = decode(blurHash, width, height) const canvas = document.createElement('canvas') canvas.width = width canvas.height = height const ctx = canvas.getContext('2d') if (ctx) { const imageData = ctx.createImageData(width, height) imageData.data.set(pixels) ctx.putImageData(imageData, 0, 0) setBlurDataUrl(canvas.toDataURL()) } } }, [blurHash]) if (hideIfError && hasError) return null return (
{isLoading && } {!hasError ? ( {alt} { setIsLoading(false) setHasError(false) setTimeout(() => setDisplayBlurHash(false), 500) }} onError={() => { setIsLoading(false) setHasError(true) }} /> ) : (
)} {displayBlurHash && blurDataUrl && !hasError && ( {alt} )}
) } const DIGITS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~' function decodeBlurHashSize(blurHash: string) { const sizeValue = DIGITS.indexOf(blurHash[0]) const numY = (sizeValue / 9 + 1) | 0 const numX = (sizeValue % 9) + 1 return { numX, numY } }