import { forwardRef, useMemo } from 'react' import type { VariantProps } from '@payfit/unity-themes' import type { ImgHTMLAttributes } from 'react' import type { UnityIllustrationAsset } from '../../generated/illustrationAssets' import { uyTv } from '@payfit/unity-themes' export const illustratedPicture = uyTv({ base: 'uy:object-contain uy:aspect-auto', }) export const illustratedIcon = uyTv({ base: 'uy:object-contain uy:aspect-square', variants: { size: { sm: 'uy:size-7', md: 'uy:size-9', lg: 'uy:size-12', }, }, }) interface IllustrationPropsCore extends Omit< ImgHTMLAttributes, 'src' | 'children' | 'alt' > { /** * The illustration asset to render. Must be imported from @payfit/unity-illustrations */ src: UnityIllustrationAsset } interface NonDecorativeIllustrationProps extends IllustrationPropsCore { /** Whether this is decorative (hidden from screen readers) */ isDecorative?: false | undefined /** Accessible name for the illustration - required when not decorative */ alt: string /** Optional description for complex illustrations */ description?: string } interface DecorativeIllustrationProps extends IllustrationPropsCore { /** Whether this is decorative (hidden from screen readers) */ isDecorative: true /** Accessible name for the illustration - optional when decorative */ alt?: string } export type IllustrationPropsBase = | NonDecorativeIllustrationProps | DecorativeIllustrationProps export type IllustratedPictureProps = IllustrationPropsBase & VariantProps & { /** * The illustration variant. It can be a regular picture or an illustrated icon */ variant: 'picture' } export type IllustratedIconProps = IllustrationPropsBase & VariantProps & { /** * The illustration variant. It can be a regular picture or an illustrated icon */ variant: 'icon' /** * The size of the illustration. It only applies to illustrated icons */ size: VariantProps['size'] } export type IllustrationProps = IllustratedPictureProps | IllustratedIconProps /** * Renders illustrations from the Unity design system with consistent styling and accessibility features. * * The Illustration component provides a standardized way to display SVG illustrations and images * , ensuring consistent sizing, responsive behavior, and accessibility compliance. * @param {IllustrationProps} props - Component props including variant, src, size, and standard HTML attributes * @see {@link IllustrationProps} for all available props * @example * ```tsx * import { Illustration } from '@payfit/unity-illustrations' * import FAQ from '@payfit/unity-illustrations/FAQ' * * function Example() { * return ( * * ) * } * ``` * @remarks * You must import illustration assets individually from `@payfit/unity-illustrations/[AssetName]` to use with this component. * See the [usage documentation](https://unity-illustrations.payfit.io/?path=/docs/introduction-usage--docs) for complete examples and best practices. */ export const Illustration = forwardRef( ( { src, alt, isDecorative: decorative = false, variant, className, style, ...props }, ref, ) => { const clsx = variant === 'picture' ? illustratedPicture({ className }) : illustratedIcon({ className, size: (props as IllustratedIconProps).size, }) const description = decorative ? (props as NonDecorativeIllustrationProps).description : undefined // Apply max dimensions for animated assets and generate CSS custom properties const { enhancedStyle, enhancedClassName } = useMemo(() => { if ('category' in src && 'dimensions' in src) { const animationClasses = 'uy:max-w-[var(--uy-illustration-max-width)] uy:max-h-[var(--uy-illustration-max-height)] uy:w-full uy:h-auto' return { enhancedStyle: { '--uy-illustration-max-width': `${src.dimensions.width}px`, '--uy-illustration-max-height': `${src.dimensions.height}px`, ...style, }, enhancedClassName: `${clsx} ${animationClasses}`.trim(), } } return { enhancedStyle: style, enhancedClassName: clsx, } }, [src, style, clsx]) return ( {decorative ) }, ) Illustration.displayName = 'Illustration'