xref: /expo/packages/expo-image/src/Image.types.ts (revision 6eefacaa)
1import { ImageStyle as RNImageStyle, ViewProps, StyleProp, ViewStyle } from 'react-native';
2
3export type ImageSource = {
4  /**
5   * A string representing the resource identifier for the image,
6   * which could be an http address, a local file path, or the name of a static image resource.
7   */
8  uri?: string;
9  /**
10   * An object representing the HTTP headers to send along with the request for a remote image.
11   * @platform android
12   * @platform ios
13   */
14  headers?: Record<string, string>;
15  /**
16   * Can be specified if known at build time, in which case the value
17   * will be used to set the default `<Image/>` component dimension
18   */
19  width?: number;
20  /**
21   * Can be specified if known at build time, in which case the value
22   * will be used to set the default `<Image/>` component dimension
23   */
24  height?: number;
25
26  /**
27   * The blurhash string to use to generate the image. You can read more about the blurhash
28   * on [`woltapp/blurhash`](https://github.com/woltapp/blurhash) repo. Ignored when `uri` is provided.
29   * When using the blurhash, you should also provide `width` and `height` (higher values reduce performance),
30   * otherwise their default value is `16`.
31   */
32  blurhash?: string;
33
34  /**
35   * The thumbhash string to use to generate the image placeholder. You can read more about thumbhash
36   * on the [`thumbhash website`](https://evanw.github.io/thumbhash/). Ignored when `uri` is provided.
37   */
38  thumbhash?: string;
39
40  /**
41   * The cache key used to query and store this specific image.
42   * If not provided, the `uri` is used also as the cache key.
43   */
44  cacheKey?: string;
45  /**
46   * The max width of the viewport for which this source should be selected.
47   * Has no effect if `source` prop is not an array or has only 1 element.
48   * Has no effect if `responsivePolicy` is not set to `static`.
49   * Ignored if `blurhash` or `thumbhash` is provided (image hashes are never selected if passed in an array).
50   * @platform web
51   */
52  webMaxViewportWidth?: number;
53};
54
55/**
56 * @hidden
57 */
58export type ImageStyle = RNImageStyle;
59
60/**
61 * Determines how the image should be resized to fit its container.
62 * @hidden Described in the {@link ImageProps['contentFit']}
63 */
64export type ImageContentFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
65
66/**
67 * Some props are from React Native Image that Expo Image supports (more or less) for easier migration,
68 * but all of them are deprecated and might be removed in the future.
69 */
70export interface ImageProps extends ViewProps {
71  /** @hidden */
72  style?: StyleProp<RNImageStyle>;
73
74  /**
75   * The image source, either a remote URL, a local file resource or a number that is the result of the `require()` function.
76   * When provided as an array of sources, the source that fits best into the container size and is closest to the screen scale
77   * will be chosen. In this case it is important to provide `width`, `height` and `scale` properties.
78   */
79  source?: ImageSource | string | number | ImageSource[] | string[] | null;
80
81  /**
82   * An image to display while loading the proper image and no image has been displayed yet or the source is unset.
83   */
84  placeholder?: ImageSource | string | number | ImageSource[] | string[] | null;
85
86  /**
87   * Determines how the image should be resized to fit its container. This property tells the image to fill the container
88   * in a variety of ways; such as "preserve that aspect ratio" or "stretch up and take up as much space as possible".
89   * It mirrors the CSS [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) property.
90   *
91   * - `'cover'` - The image is sized to maintain its aspect ratio while filling the container box.
92   * If the image's aspect ratio does not match the aspect ratio of its box, then the object will be clipped to fit.
93   *
94   * - `'contain'` - The image is scaled down or up to maintain its aspect ratio while fitting within the container box.
95   *
96   * - `'fill'` - The image is sized to entirely fill the container box. If necessary, the image will be stretched or squished to fit.
97   *
98   * - `'none'` - The image is not resized and is centered by default.
99   * When specified, the exact position can be controlled with [`contentPosition`](#contentposition) prop.
100   *
101   * - `'scale-down'` - The image is sized as if `none` or `contain` were specified, whichever would result in a smaller concrete image size.
102   *
103   * @default 'cover'
104   */
105  contentFit?: ImageContentFit;
106
107  /**
108   * Determines how the placeholder should be resized to fit its container
109   * @hidden Described in the {@link ImageProps['contentFit']}
110   * @default 'scale-down'
111   */
112  placeholderContentFit?: ImageContentFit;
113
114  /**
115   * It is used together with [`contentFit`](#contentfit) to specify how the image should be positioned with x/y coordinates inside its own container.
116   * An equivalent of the CSS [`object-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) property.
117   * @default 'center'
118   */
119  contentPosition?: ImageContentPosition;
120
121  /**
122   * Describes how the image view should transition the contents when switching the image source.\
123   * If provided as a number, it is the duration in milliseconds of the `'cross-dissolve'` effect.
124   */
125  transition?: ImageTransition | number | null;
126
127  /**
128   * The radius of the blur in points, `0` means no blur effect.
129   * This effect is not applied to placeholders.
130   * @default 0
131   */
132  blurRadius?: number;
133
134  /**
135   * A color used to tint template images (a bitmap image where only the opacity matters).
136   * The color is applied to every non-transparent pixel, causing the image’s shape to adopt that color.
137   * This effect is not applied to placeholders.
138   * @default null
139   */
140  tintColor?: string | null;
141
142  /**
143   * Priorities for completing loads. If more than one load is queued at a time,
144   * the load with the higher priority will be started first.
145   * Priorities are considered best effort, there are no guarantees about the order in which loads will start or finish.
146   * @default 'normal'
147   */
148  priority?: 'low' | 'normal' | 'high' | null;
149
150  /**
151   * Determines whether to cache the image and where: on the disk, in the memory or both.
152   *
153   * - `'none'` - Image is not cached at all.
154   *
155   * - `'disk'` - Image is queried from the disk cache if exists, otherwise it's downloaded and then stored on the disk.
156   *
157   * - `'memory'` - Image is cached in memory. Might be useful when you render a high-resolution picture many times.
158   * Memory cache may be purged very quickly to prevent high memory usage and the risk of out of memory exceptions.
159   *
160   * - `'memory-disk'` - Image is cached in memory, but with a fallback to the disk cache.
161   *
162   * @default 'disk'
163   */
164  cachePolicy?: 'none' | 'disk' | 'memory' | 'memory-disk' | /** @hidden */ null;
165
166  /**
167   * Controls the selection of the image source based on the container or viewport size on the web.
168   *
169   * If set to `'static'`, the browser selects the correct source based on user's viewport width. Works with static rendering.
170   * Make sure to set the `'webMaxViewportWidth'` property on each source for best results.
171   * For example, if an image occupies 1/3 of the screen width, set the `'webMaxViewportWidth'` to 3x the image width.
172   * The source with the largest `'webMaxViewportWidth'` is used even for larger viewports.
173   *
174   * If set to `'initial'`, the component will select the correct source during mount based on container size. Does not work with static rendering.
175   *
176   * If set to `'live'`, the component will select the correct source on every resize based on container size. Does not work with static rendering.
177   *
178   * @default 'static'
179   * @platform web
180   */
181  responsivePolicy?: 'live' | 'initial' | 'static';
182
183  /**
184   * Changing this prop resets the image view content to blank or a placeholder before loading and rendering the final image.
185   * This is especially useful for any kinds of recycling views like [FlashList](https://github.com/shopify/flash-list)
186   * to prevent showing the previous source before the new one fully loads.
187   * @default null
188   * @platform android
189   * @platform ios
190   */
191  recyclingKey?: string | null;
192
193  /**
194   * Called when the image starts to load.
195   */
196  onLoadStart?: () => void;
197
198  /**
199   * Called when the image load completes successfully.
200   */
201  onLoad?: (event: ImageLoadEventData) => void;
202
203  /**
204   * Called when the image is loading. Can be called multiple times before the image has finished loading.
205   * The event object provides details on how many bytes were loaded so far and what's the expected total size.
206   */
207  onProgress?: (event: ImageProgressEventData) => void;
208
209  /**
210   * Called on an image fetching error.
211   */
212  onError?: (event: ImageErrorEventData) => void;
213
214  /**
215   * Called when the image load either succeeds or fails.
216   */
217  onLoadEnd?: () => void;
218
219  // DEPRECATED
220
221  /**
222   * @deprecated Provides compatibility for [`defaultSource` from React Native Image](https://reactnative.dev/docs/image#defaultsource).
223   * Use [`placeholder`](#placeholder) prop instead.
224   */
225  defaultSource?: ImageSource | null;
226
227  /**
228   * @deprecated Provides compatibility for [`loadingIndicatorSource` from React Native Image](https://reactnative.dev/docs/image#loadingindicatorsource).
229   * Use [`placeholder`](#placeholder) prop instead.
230   */
231  loadingIndicatorSource?: ImageSource | null;
232
233  /**
234   * @deprecated Provides compatibility for [`resizeMode` from React Native Image](https://reactnative.dev/docs/image#resizemode).
235   * Note that `"repeat"` option is not supported at all.
236   * Use the more powerful [`contentFit`](#contentfit) and [`contentPosition`](#contentposition) props instead.
237   */
238  resizeMode?: 'cover' | 'contain' | 'stretch' | 'repeat' | 'center';
239
240  /**
241   * @deprecated Provides compatibility for [`fadeDuration` from React Native Image](https://reactnative.dev/docs/image#fadeduration-android).
242   * Instead use [`transition`](#transition) with the provided duration.
243   */
244  fadeDuration?: number;
245
246  /**
247   * Whether this View should be focusable with a non-touch input device and receive focus with a hardware keyboard.
248   * @default false
249   * @platform android
250   */
251  focusable?: boolean;
252
253  /**
254   * When true, indicates that the view is an accessibility element.
255   * When a view is an accessibility element, it groups its children into a single selectable component.
256   *
257   * On Android, the `accessible` property will be translated into the native `isScreenReaderFocusable`,
258   * so it's only affecting the screen readers behaviour.
259   * @default false
260   * @platform android
261   * @platform ios
262   */
263  accessible?: boolean;
264
265  /**
266   * The text that's read by the screen reader when the user interacts with the image. Sets the the `alt` tag on web which is used for web crawlers and link traversal.
267   * @default undefined
268   */
269  accessibilityLabel?: string;
270
271  /**
272   * The text that's read by the screen reader when the user interacts with the image. Sets the the `alt` tag on web which is used for web crawlers and link traversal. Is an alias for `accessibilityLabel`.
273   *
274   * @alias accessibilityLabel
275   * @default undefined
276   */
277  alt?: string;
278
279  /**
280   * Enables Live Text interaction with the image. Check official [Apple documentation](https://developer.apple.com/documentation/visionkit/enabling_live_text_interactions_with_images) for more details.
281   * @default false
282   * @platform ios 16.0+
283   */
284  enableLiveTextInteraction?: boolean;
285
286  /**
287   * Whether the image should be downscaled to match the size of the view container.
288   * Turning off this functionality could negatively impact the application's performance, particularly when working with large assets.
289   * However, it would result in smoother image resizing, and end-users would always have access to the highest possible asset quality.
290   *
291   * Downscaling is never used when the `contentFit` prop is set to `none` or `fill`.
292   * @default true
293   * @platform android
294   */
295  allowDownscaling?: boolean;
296}
297
298/**
299 * It narrows down some props to types expected by the native/web side.
300 * @hidden
301 */
302export interface ImageNativeProps extends ImageProps {
303  style?: RNImageStyle;
304  source?: ImageSource[];
305  placeholder?: ImageSource[];
306  contentPosition?: ImageContentPositionObject;
307  transition?: ImageTransition | null;
308}
309
310/**
311 * A value that represents the relative position of a single axis.
312 *
313 * If `number`, it is a distance in points (logical pixels) from the respective edge.\
314 * If `string`, it must be a percentage value where `'100%'` is the difference in size between the container and the image along the respective axis,
315 * or `'center'` which is an alias for `'50%'` that is the default value. You can read more regarding percentages on the MDN docs for
316 * [`background-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position#regarding_percentages) that describes this concept well.
317 */
318export type ImageContentPositionValue = number | string | `${number}%` | `${number}` | 'center';
319
320// eslint-disable
321// prettier-ignore
322/**
323 * Specifies the position of the image inside its container. One value controls the x-axis and the second value controls the y-axis.
324 *
325 * Additionally, it supports stringified shorthand form that specifies the edges to which to align the image content:\
326 * `'center'`, `'top'`, `'right'`, `'bottom'`, `'left'`, `'top center'`, `'top right'`, `'top left'`, `'right center'`, `'right top'`,
327 * `'right bottom'`, `'bottom center'`, `'bottom right'`, `'bottom left'`, `'left center'`, `'left top'`, `'left bottom'`.\
328 * If only one keyword is provided, then the other dimension is set to `'center'` (`'50%'`), so the image is placed in the middle of the specified edge.\
329 * As an example, `'top right'` is the same as `{ top: 0, right: 0 }` and `'bottom'` is the same as `{ bottom: 0, left: '50%' }`.
330 */
331export type ImageContentPosition =
332  /**
333   * An object that positions the image relatively to the top-right corner.
334   */
335  {
336    top?: ImageContentPositionValue;
337    right?: ImageContentPositionValue;
338  } |
339  /**
340   * An object that positions the image relatively to the top-left corner.
341   */
342  {
343    top?: ImageContentPositionValue;
344    left?: ImageContentPositionValue;
345  } |
346  /**
347   * An object that positions the image relatively to the bottom-right corner.
348   */
349  {
350    bottom?: ImageContentPositionValue;
351    right?: ImageContentPositionValue;
352  } |
353  /**
354   * An object that positions the image relatively to the bottom-left corner.
355   */
356  {
357    bottom?: ImageContentPositionValue;
358    left?: ImageContentPositionValue;
359  }
360  | ImageContentPositionString;
361// eslint-enable
362
363export interface ImageBackgroundProps extends Omit<ImageProps, 'style'> {
364  /** The style of the image container */
365  style?: StyleProp<ViewStyle> | undefined;
366  /** Style object for the image */
367  imageStyle?: StyleProp<RNImageStyle> | undefined;
368  /** @hidden */
369  children?: React.ReactNode | undefined;
370}
371
372/**
373 * @hidden It's described as part of {@link ImageContentPosition}.
374 */
375export type ImageContentPositionString =
376  | 'center'
377  | 'top'
378  | 'right'
379  | 'bottom'
380  | 'left'
381  | 'top center'
382  | 'top right'
383  | 'top left'
384  | 'right center'
385  | 'right top'
386  | 'right bottom'
387  | 'bottom center'
388  | 'bottom right'
389  | 'bottom left'
390  | 'left center'
391  | 'left top'
392  | 'left bottom';
393
394type OnlyObject<T> = T extends object ? T : never;
395
396/**
397 * @hidden It's a conditional type that matches only objects of {@link ImageContentPosition}.
398 */
399export type ImageContentPositionObject = OnlyObject<ImageContentPosition>;
400
401/**
402 * An object that describes the smooth transition when switching the image source.
403 */
404export type ImageTransition = {
405  /**
406   * The duration of the transition in milliseconds.
407   * @default 0
408   */
409  duration?: number;
410
411  /**
412   * Specifies the speed curve of the transition effect and how intermediate values are calculated.
413   * @default 'ease-in-out'
414   */
415  timing?: 'ease-in-out' | 'ease-in' | 'ease-out' | 'linear';
416
417  /**
418   * An animation effect used for transition.
419   * @default 'cross-dissolve'
420   *
421   * On Android, only `'cross-dissolve'` is supported.
422   * On Web, `'curl-up'` and `'curl-down'` effects are not supported.
423   */
424  effect?:
425    | 'cross-dissolve'
426    | 'flip-from-top'
427    | 'flip-from-right'
428    | 'flip-from-bottom'
429    | 'flip-from-left'
430    | 'curl-up'
431    | 'curl-down'
432    | null;
433};
434
435export type ImageLoadEventData = {
436  cacheType: 'none' | 'disk' | 'memory';
437  source: {
438    url: string;
439    width: number;
440    height: number;
441    mediaType: string | null;
442  };
443};
444
445export type ImageProgressEventData = {
446  loaded: number;
447  total: number;
448};
449
450export type ImageErrorEventData = {
451  error: string;
452};
453