import { forwardRef, useEffect, useState } from 'react' import type { UnityIllustrationAsset, UnityIllustrationName, } from '../../generated/illustrationAssets' import type { IllustratedIconProps, IllustratedPictureProps, } from '../illustration/Illustration' import { lazyIllustrationsMap } from '../../generated/illustrationAssets' import { illustratedIcon, illustratedPicture, Illustration, } from '../illustration/Illustration' /** * Props for the LazyIllustration component. * * This type is a union of two possible prop types: * 1. Props for the picture variant (omitting 'src' and adding 'name') * 2. Props for the icon variant (omitting 'src' and adding 'name') * @property {UnityIllustrationName} name - The name of the illustration to load dynamically. * This must be one of the valid illustration names from the Unity illustration library. * @property {'picture' | 'icon'} variant - The illustration variant. It can be a regular picture or an illustrated icon. * @property {string} alt - Accessible name for the illustration (required when isDecorative is false or undefined). * @property {string} [description] - Optional description for complex illustrations. * @property {boolean} [isDecorative] - Whether this is decorative (hidden from screen readers). * @property {'sm' | 'md' | 'lg'} [size] - The size of the illustration. Required when variant is 'icon'. */ export type LazyIllustrationProps = | (Omit & { name: UnityIllustrationName }) | (Omit & { name: UnityIllustrationName }) /** * A component that lazily loads illustrations by name and renders them using the Illustration component. * * The LazyIllustration component dynamically imports illustrations based on their name using the * lazyIllustrationsMap from the generated illustrationAssets file. This allows for code-splitting * and on-demand loading of illustrations, which can significantly improve initial load performance. * @param {LazyIllustrationProps} props - Component props including name, variant, and standard HTML attributes * @see {@link LazyIllustrationProps} for all available props * @example * ```tsx * import { LazyIllustration } from '@payfit/unity-illustrations' * * function Example() { * return ( * * ) * } * ``` * @example * ```tsx * // Using with icon variant * import { LazyIllustration } from '@payfit/unity-illustrations' * * function IconExample() { * return ( * * ) * } * ``` * @remarks * Unlike the Illustration component, you don't need to import illustration assets individually. * Simply provide the name of the illustration you want to use, and LazyIllustration will handle * the dynamic import for you. The component returns null during loading, so you may want to * provide your own loading state or placeholder. */ export const LazyIllustration = forwardRef< HTMLImageElement, LazyIllustrationProps >(({ name, ...props }, ref) => { const [illustration, setIllustration] = useState(null) const [isLoading, setIsLoading] = useState(true) useEffect(() => { const loadIllustration = async () => { try { setIsLoading(true) const importFn = lazyIllustrationsMap[name] const module = await importFn() setIllustration(module.default) } catch (error) { console.error(`Failed to load illustration "${name}":`, error) } finally { setIsLoading(false) } } void loadIllustration() }, [name]) if (isLoading || !illustration) { const clsx = props.variant === 'picture' ? illustratedPicture({ className: props.className }) : illustratedIcon({ className: props.className, size: (props as IllustratedIconProps).size, }) return
} // Handle the discriminated union by using type assertion // This is safe because LazyIllustrationProps inherits from IllustrationProps (excluding src and name) // eslint-disable-next-line @typescript-eslint/no-explicit-any return }) LazyIllustration.displayName = 'LazyIllustration'