/**
* 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';