import * as React from "react"; import classNames from "classnames"; import { Text } from "#gdq/index"; import { useSetRef } from "#gdq/utils/RefUtils"; import { Image as ImageIcon } from "@faulty/gdq-icons/icons/Image"; import styles from "./Image.module.css"; export enum ImageLoadState { INITIALIZED, LOADING, LOADED, ERROR, } export interface ImageProps { src: string; alt?: string; className?: string; // Native passthrough props width?: number; height?: number; crossOrigin?: React.ImgHTMLAttributes["crossOrigin"]; decoding?: React.ImgHTMLAttributes["decoding"]; /** * Either 'eager' or 'lazy' indicates how the browser should prioritize * loading this image. */ loadingMode?: React.ImgHTMLAttributes["loading"]; referrerPolicy?: React.ImgHTMLAttributes["referrerPolicy"]; } export const Image = React.forwardRef(function Image( props: ImageProps, ref: React.ForwardedRef, ) { const { src, alt, className, ...nativeProps } = props; const imageRef = React.useRef(null); const [loadState, setLoadState] = React.useState(ImageLoadState.INITIALIZED); const setRef = useSetRef(imageRef, ref); React.useLayoutEffect(() => { const image = imageRef.current; if (image == null) return; setLoadState(ImageLoadState.LOADING); function handleLoad(_event: Event) { setLoadState(ImageLoadState.LOADED); } function handleError(_event: ErrorEvent) { setLoadState(ImageLoadState.ERROR); } image.addEventListener("load", handleLoad); image.addEventListener("error", handleError); return () => { image.removeEventListener("load", handleLoad); image.removeEventListener("error", handleError); }; }, []); const inner = (() => { switch (loadState) { case ImageLoadState.INITIALIZED: case ImageLoadState.LOADING: return null; case ImageLoadState.ERROR: return ( <> {alt != null ? ( {alt} ) : null} ); case ImageLoadState.LOADED: return; } })(); return (
{inner} {alt}
); });