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 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 11x 14x 14x 14x 14x 14x 3x 3x 3x 14x 14x 11x 3x 3x 3x 11x 8x 8x 11x 14x 14x 3x 3x 3x 14x 4x 4x 4x 4x 3x 3x 2x 2x 4x 2x 2x 4x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x | 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}
/>
);
};
|