Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 1x 1x 1x 1x 1x 415x 415x 415x 415x 415x 415x 415x 415x 415x 415x 415x 194x 415x 415x 415x 415x 197x 3x 3x 3x 197x 415x 194x 3x 3x 3x 194x 191x 191x 194x 415x 415x 3x 3x 3x 415x 4x 4x 4x 4x 3x 3x 2x 2x 4x 2x 2x 4x 415x 415x 415x 415x 415x 415x 415x 415x 415x 415x 415x 415x | import {
ImgHTMLAttributes,
useState,
useEffect,
type SyntheticEvent,
useRef,
} from 'react';
// Constants
import { BLUR_SRC, FALLBACK_SRC } from '@shared/constants';
// Types
import { Placeholder } from '@shared/types';
// Utils
import { combineClasses } from '@shared/utils';
type BaseImgProps = Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'>;
interface ImageProps extends BaseImgProps {
src: string | Blob;
alt: string;
fallbackSrc?: string;
blurDataURL?: string;
placeholder?: Placeholder;
width?: number;
height?: number;
}
export const ImageFallback = (props: ImageProps) => {
const {
src,
alt,
fallbackSrc = FALLBACK_SRC.DEFAULT,
blurDataURL = BLUR_SRC.DEFAULT,
placeholder,
width,
height,
...rest
} = props;
const [imgSrc, setImgSrc] = useState<string>(() =>
src instanceof Blob ? URL.createObjectURL(src) : src,
);
const objectUrlRef = useRef<string | null>(null);
const shouldUseBlur = placeholder === Placeholder.Blur && blurDataURL;
const revokeBlobUrl = () => {
if (objectUrlRef.current) {
URL.revokeObjectURL(objectUrlRef.current);
objectUrlRef.current = null;
}
};
useEffect(() => {
if (src instanceof Blob) {
const blobUrl = URL.createObjectURL(src);
setImgSrc(blobUrl);
objectUrlRef.current = blobUrl;
} else {
setImgSrc(src);
}
return revokeBlobUrl;
}, [src]);
const handleError = () => {
revokeBlobUrl();
setImgSrc(fallbackSrc);
};
const handleLoad = (e: SyntheticEvent<HTMLImageElement>) => {
const img = e.currentTarget;
const isBroken =
!img.complete ||
!img.naturalWidth ||
!img.naturalHeight ||
(objectUrlRef.current &&
imgSrc === objectUrlRef.current &&
(!img.width || !img.height));
if (isBroken) {
handleError();
}
};
return (
<img
src={imgSrc}
alt={alt}
width={width}
height={height}
onLoad={handleLoad}
onError={handleError}
className={combineClasses(shouldUseBlur && 'blur-md', rest.className)}
{...rest}
/>
);
};
|