/** * Thin wrapper around React Native's `Image`. * * Design-system layout (sizes, radii, loading/error UI) is intentionally * NOT baked in here — use purpose-built components like `Avatar` instead. * * What this wrapper adds over a raw RN `Image`: * - Accepts a plain URI **string** as `source` (auto-normalized to `{ uri }`). * - Extends the prop surface with **web-only** HTML attributes (`sizes`, * `loading`, `decoding`, etc.) for better `react-native-web` support. * Props already covered by RN 0.81+ (`alt`, `srcSet`, `crossOrigin`, * `referrerPolicy`) are inherited from the native typings. * - All other props and `ref` are forwarded to the underlying `Image`. * * Inspired by Expo's html-elements Image primitive: * @see https://github.com/expo/expo/blob/main/packages/html-elements/src/primitives/Image.tsx */ import { forwardRef, type ComponentPropsWithoutRef, type ComponentRef } from 'react'; import { Image as RNImage, type ImageSourcePropType } from 'react-native'; /** Coerces a plain URI string into the `{ uri }` shape RN expects. */ function normalizeSource(source: string | ImageSourcePropType): ImageSourcePropType { if (typeof source === 'string') { return { uri: source }; } return source; } // --------------------------------------------------------------------------- // Web-oriented props not yet covered by react-native's Image typings. // Props already present in RN 0.81+ (alt, srcSet, crossOrigin, // referrerPolicy) are inherited from the native Image props. // --------------------------------------------------------------------------- /** * HTML-ish attributes not in RN’s core typings. Forwarded to `Image` with `...props`; * **react-native-web** maps supported ones to the DOM ``. On iOS/Android they are * typically ignored (no visual effect). */ export interface WebImageProps { /** DOM `sizes` for responsive selection with `srcSet` — web only. */ sizes?: string; /** DOM lazy-loading hint — web only. */ loading?: 'lazy' | 'eager'; /** DOM decode hint — web only. */ decoding?: 'async' | 'auto' | 'sync'; /** DOM drag behavior — web only. */ draggable?: boolean; /** DOM focus order — web only. */ tabIndex?: number; } // --------------------------------------------------------------------------- // ImageProps = native Image props (minus `source`) + WebImageProps + our overrides. // `source` is re-declared to also accept a plain string. // --------------------------------------------------------------------------- export type ImageProps = WebImageProps & Omit, 'source'> & { /** URI string (normalized to `{ uri }`) or standard `ImageSourcePropType`. */ source: string | ImageSourcePropType; className?: string; }; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export const Image = forwardRef, ImageProps>(function Image( { source, ...props }, ref, ) { return ; }); Image.displayName = 'Image';