Add Image and ImageBackground to buildTypes and align Flow with TS defs (#49659)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49659

Changelog:
[Internal] - Added Image and ImageBackground to buildTypes and aligned Flow with TS defs

Reviewed By: huntie

Differential Revision: D70099485

fbshipit-source-id: 6000fc7099a4f811c451a08c4d5cf505d84ec0c9
This commit is contained in:
Dawid Małecki
2025-02-25 09:12:52 -08:00
committed by Facebook GitHub Bot
parent 659004f16b
commit 7856ef8409
7 changed files with 188 additions and 70 deletions
@@ -138,7 +138,7 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
props.loadingIndicatorSource,
);
if (props.children) {
if (props.children != null) {
throw new Error(
'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.',
);
@@ -10,4 +10,18 @@
import type {Image} from './ImageTypes.flow';
export type {
ImageProgressEventDataIOS,
ImagePropsIOS,
ImagePropsAndroid,
ImageSourcePropType,
ImageLoadEventData,
ImageErrorEventData,
ImagePropsBase,
ImageProps,
ImageBackgroundProps,
} from './ImageProps';
export type {ImageResolvedAssetSource, ImageSize} from './ImageTypes.flow';
declare export default Image;
+97 -30
View File
@@ -23,20 +23,36 @@ import type {
} from '../Types/CoreEventTypes';
import typeof Image from './Image';
import type {ImageResizeMode} from './ImageResizeMode';
import type {ImageSource} from './ImageSource';
import type {ImageSource, ImageURISource} from './ImageSource';
import type {ElementRef, Node, RefSetter} from 'react';
export type ImageSourcePropType = ImageSource;
/**
* @see ImagePropsIOS.onProgress
*/
export type ImageProgressEventDataIOS = {
loaded: number,
total: number,
};
export type ImageErrorEventData = {
error: string,
};
export type ImageLoadEventData = {
source: {
height: number,
width: number,
uri: string,
},
};
export type ImageLoadEvent = NativeSyntheticEvent<
$ReadOnly<{
source: $ReadOnly<{
width: number,
height: number,
uri: string,
}>,
}>,
$ReadOnly<ImageLoadEventData>,
>;
type IOSImageProps = $ReadOnly<{
export type ImagePropsIOS = $ReadOnly<{
/**
* A static image to display while loading the image source.
*
@@ -55,19 +71,42 @@ type IOSImageProps = $ReadOnly<{
* See https://reactnative.dev/docs/image#onprogress
*/
onProgress?: ?(
event: NativeSyntheticEvent<$ReadOnly<{loaded: number, total: number}>>,
event: NativeSyntheticEvent<$ReadOnly<ImageProgressEventDataIOS>>,
) => void,
}>;
type AndroidImageProps = $ReadOnly<{
loadingIndicatorSource?: ?(number | $ReadOnly<{uri: string}>),
export type ImagePropsAndroid = $ReadOnly<{
/**
* similarly to `source`, this property represents the resource used to render
* the loading indicator for the image, displayed until image is ready to be
* displayed, typically after when it got downloaded from network.
*/
loadingIndicatorSource?: ?(number | $ReadOnly<ImageURISource>),
progressiveRenderingEnabled?: ?boolean,
fadeDuration?: ?number,
/**
* The mechanism that should be used to resize the image when the image's
* dimensions differ from the image view's dimensions. Defaults to `'auto'`.
* See https://reactnative.dev/docs/image#resizemethod-android
* The mechanism that should be used to resize the image when the image's dimensions
* differ from the image view's dimensions. Defaults to `auto`.
*
* - `auto`: Use heuristics to pick between `resize` and `scale`.
*
* - `resize`: A software operation which changes the encoded image in memory before it
* gets decoded. This should be used instead of `scale` when the image is much larger
* than the view.
*
* - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is
* faster (usually hardware accelerated) and produces higher quality images. This
* should be used if the image is smaller than the view. It should also be used if the
* image is slightly bigger than the view.
*
* - `none`: No sampling is performed and the image is displayed in its full resolution. This
* should only be used in rare circumstances because it is considered unsafe as Android will
* throw a runtime exception when trying to render images that consume too much memory.
*
* More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html.
*
* @platform android
*/
resizeMethod?: ?('auto' | 'resize' | 'scale' | 'none'),
@@ -81,11 +120,8 @@ type AndroidImageProps = $ReadOnly<{
resizeMultiplier?: ?number,
}>;
export type ImageProps = $ReadOnly<{
export type ImagePropsBase = $ReadOnly<{
...$Diff<ViewProps, $ReadOnly<{style: ?ViewStyleProp}>>,
...IOSImageProps,
...AndroidImageProps,
/**
* When true, indicates the image is an accessibility element.
*
@@ -166,16 +202,15 @@ export type ImageProps = $ReadOnly<{
* See https://reactnative.dev/docs/image#onerror
*/
onError?: ?(
event: NativeSyntheticEvent<
$ReadOnly<{
error: string,
}>,
>,
event: NativeSyntheticEvent<$ReadOnly<ImageErrorEventData>>,
) => void,
/**
* onLayout function
*
* Invoked on mount and layout changes with
* `{nativeEvent: {layout: {x, y, width, height}}}`.
*
* {nativeEvent: { layout: {x, y, width, height} }}.
*
* See https://reactnative.dev/docs/image#onlayout
*/
@@ -206,15 +241,16 @@ export type ImageProps = $ReadOnly<{
/**
* The image source (either a remote URL or a local file resource).
*
* This prop can also contain several remote URLs, specified together with their width and height and potentially with scale/other URI arguments.
* The native side will then choose the best uri to display based on the measured size of the image container.
* A cache property can be added to control how networked request interacts with the local cache.
*
* The currently supported formats are png, jpg, jpeg, bmp, gif, webp (Android only), psd (iOS only).
*
* See https://reactnative.dev/docs/image#source
*/
source?: ?ImageSource,
/**
* See https://reactnative.dev/docs/image#style
*/
style?: ?ImageStyleProp,
/**
* A string indicating which referrer to use when fetching the resource.
* Similar to referrerpolicy from HTML.
@@ -236,6 +272,26 @@ export type ImageProps = $ReadOnly<{
* Determines how to resize the image when the frame doesn't match the raw
* image dimensions.
*
* 'cover': Scale the image uniformly (maintain the image's aspect ratio)
* so that both dimensions (width and height) of the image will be equal
* to or larger than the corresponding dimension of the view (minus padding).
*
* 'contain': Scale the image uniformly (maintain the image's aspect ratio)
* so that both dimensions (width and height) of the image will be equal to
* or less than the corresponding dimension of the view (minus padding).
*
* 'stretch': Scale width and height independently, This may change the
* aspect ratio of the src.
*
* 'repeat': Repeat the image to cover the frame of the view.
* The image will keep it's size and aspect ratio. (iOS only)
*
* 'center': Scale the image down so that it is completely visible,
* if bigger than the area of the view.
* The image will not be scaled up.
*
* 'none': Do not resize the image. The image will be displayed at its intrinsic size.
*
* See https://reactnative.dev/docs/image#resizemode
*/
resizeMode?: ?ImageResizeMode,
@@ -272,6 +328,17 @@ export type ImageProps = $ReadOnly<{
children?: empty,
}>;
export type ImageProps = $ReadOnly<{
...ImagePropsIOS,
...ImagePropsAndroid,
...ImagePropsBase,
/**
* See https://reactnative.dev/docs/image#style
*/
style?: ?ImageStyleProp,
}>;
export type ImageBackgroundProps = $ReadOnly<{
...ImageProps,
children?: Node,
@@ -17,8 +17,15 @@ import typeof TextInlineImageNativeComponent from './TextInlineImageNativeCompon
import * as React from 'react';
export type ImageSize = {
width: number,
height: number,
};
export type ImageResolvedAssetSource = ResolvedAssetSource;
type ImageComponentStaticsIOS = $ReadOnly<{
getSize(uri: string): Promise<{width: number, height: number}>,
getSize(uri: string): Promise<ImageSize>,
getSize(
uri: string,
success: (width: number, height: number) => void,
@@ -28,7 +35,7 @@ type ImageComponentStaticsIOS = $ReadOnly<{
getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
): Promise<{width: number, height: number}>,
): Promise<ImageSize>,
getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
@@ -46,9 +53,12 @@ type ImageComponentStaticsIOS = $ReadOnly<{
queryCache(
urls: Array<string>,
): Promise<{[string]: 'memory' | 'disk' | 'disk/memory', ...}>,
): Promise<{[url: string]: 'memory' | 'disk' | 'disk/memory', ...}>,
resolveAssetSource(source: ImageSource): ?ResolvedAssetSource,
/**
* @see https://reactnative.dev/docs/image#resolveassetsource
*/
resolveAssetSource(source: ImageSource): ?ImageResolvedAssetSource,
}>;
type ImageComponentStaticsAndroid = $ReadOnly<{
@@ -57,7 +67,7 @@ type ImageComponentStaticsAndroid = $ReadOnly<{
}>;
export type AbstractImageAndroid = component(
ref: React.RefSetter<
ref?: React.RefSetter<
| React.ElementRef<TextInlineImageNativeComponent>
| React.ElementRef<ImageViewNativeComponent>,
>,
@@ -67,7 +77,7 @@ export type AbstractImageAndroid = component(
export type ImageAndroid = AbstractImageAndroid & ImageComponentStaticsAndroid;
export type AbstractImageIOS = component(
ref: React.RefSetter<React.ElementRef<ImageViewNativeComponent>>,
ref?: React.RefSetter<React.ElementRef<ImageViewNativeComponent>>,
...props: ImagePropsType
);
@@ -4470,7 +4470,19 @@ declare export function getUrlCacheBreaker(): string;
`;
exports[`public API should not change unintentionally Libraries/Image/Image.js.flow 1`] = `
"declare export default Image;
"export type {
ImageProgressEventDataIOS,
ImagePropsIOS,
ImagePropsAndroid,
ImageSourcePropType,
ImageLoadEventData,
ImageErrorEventData,
ImagePropsBase,
ImageProps,
ImageBackgroundProps,
} from \\"./ImageProps\\";
export type { ImageResolvedAssetSource, ImageSize } from \\"./ImageTypes.flow\\";
declare export default Image;
"
`;
@@ -4515,33 +4527,40 @@ declare export function useWrapRefWithImageAttachedCallbacks(
`;
exports[`public API should not change unintentionally Libraries/Image/ImageProps.js 1`] = `
"export type ImageLoadEvent = NativeSyntheticEvent<
$ReadOnly<{
source: $ReadOnly<{
width: number,
height: number,
uri: string,
}>,
}>,
"export type ImageSourcePropType = ImageSource;
export type ImageProgressEventDataIOS = {
loaded: number,
total: number,
};
export type ImageErrorEventData = {
error: string,
};
export type ImageLoadEventData = {
source: {
height: number,
width: number,
uri: string,
},
};
export type ImageLoadEvent = NativeSyntheticEvent<
$ReadOnly<ImageLoadEventData>,
>;
type IOSImageProps = $ReadOnly<{
export type ImagePropsIOS = $ReadOnly<{
defaultSource?: ?ImageSource,
onPartialLoad?: ?() => void,
onProgress?: ?(
event: NativeSyntheticEvent<$ReadOnly<{ loaded: number, total: number }>>
event: NativeSyntheticEvent<$ReadOnly<ImageProgressEventDataIOS>>
) => void,
}>;
type AndroidImageProps = $ReadOnly<{
loadingIndicatorSource?: ?(number | $ReadOnly<{ uri: string }>),
export type ImagePropsAndroid = $ReadOnly<{
loadingIndicatorSource?: ?(number | $ReadOnly<ImageURISource>),
progressiveRenderingEnabled?: ?boolean,
fadeDuration?: ?number,
resizeMethod?: ?(\\"auto\\" | \\"resize\\" | \\"scale\\" | \\"none\\"),
resizeMultiplier?: ?number,
}>;
export type ImageProps = $ReadOnly<{
export type ImagePropsBase = $ReadOnly<{
...$Diff<ViewProps, $ReadOnly<{ style: ?ViewStyleProp }>>,
...IOSImageProps,
...AndroidImageProps,
accessible?: ?boolean,
internal_analyticTag?: ?string,
accessibilityLabel?: ?Stringish,
@@ -4554,18 +4573,13 @@ export type ImageProps = $ReadOnly<{
height?: number,
width?: number,
onError?: ?(
event: NativeSyntheticEvent<
$ReadOnly<{
error: string,
}>,
>
event: NativeSyntheticEvent<$ReadOnly<ImageErrorEventData>>
) => void,
onLayout?: ?(event: LayoutChangeEvent) => mixed,
onLoad?: ?(event: ImageLoadEvent) => void,
onLoadEnd?: ?() => void,
onLoadStart?: ?() => void,
source?: ?ImageSource,
style?: ?ImageStyleProp,
referrerPolicy?: ?(
| \\"no-referrer\\"
| \\"no-referrer-when-downgrade\\"
@@ -4583,6 +4597,12 @@ export type ImageProps = $ReadOnly<{
srcSet?: ?string,
children?: empty,
}>;
export type ImageProps = $ReadOnly<{
...ImagePropsIOS,
...ImagePropsAndroid,
...ImagePropsBase,
style?: ?ImageStyleProp,
}>;
export type ImageBackgroundProps = $ReadOnly<{
...ImageProps,
children?: Node,
@@ -4646,8 +4666,13 @@ exports[`public API should not change unintentionally Libraries/Image/ImageSourc
`;
exports[`public API should not change unintentionally Libraries/Image/ImageTypes.flow.js 1`] = `
"type ImageComponentStaticsIOS = $ReadOnly<{
getSize(uri: string): Promise<{ width: number, height: number }>,
"export type ImageSize = {
width: number,
height: number,
};
export type ImageResolvedAssetSource = ResolvedAssetSource;
type ImageComponentStaticsIOS = $ReadOnly<{
getSize(uri: string): Promise<ImageSize>,
getSize(
uri: string,
success: (width: number, height: number) => void,
@@ -4656,7 +4681,7 @@ exports[`public API should not change unintentionally Libraries/Image/ImageTypes
getSizeWithHeaders(
uri: string,
headers: { [string]: string, ... }
): Promise<{ width: number, height: number }>,
): Promise<ImageSize>,
getSizeWithHeaders(
uri: string,
headers: { [string]: string, ... },
@@ -4671,15 +4696,15 @@ exports[`public API should not change unintentionally Libraries/Image/ImageTypes
): Promise<boolean>,
queryCache(
urls: Array<string>
): Promise<{ [string]: \\"memory\\" | \\"disk\\" | \\"disk/memory\\", ... }>,
resolveAssetSource(source: ImageSource): ?ResolvedAssetSource,
): Promise<{ [url: string]: \\"memory\\" | \\"disk\\" | \\"disk/memory\\", ... }>,
resolveAssetSource(source: ImageSource): ?ImageResolvedAssetSource,
}>;
type ImageComponentStaticsAndroid = $ReadOnly<{
...ImageComponentStaticsIOS,
abortPrefetch(requestId: number): void,
}>;
export type AbstractImageAndroid = component(
ref: React.RefSetter<
ref?: React.RefSetter<
| React.ElementRef<TextInlineImageNativeComponent>
| React.ElementRef<ImageViewNativeComponent>,
>,
@@ -4687,7 +4712,7 @@ export type AbstractImageAndroid = component(
);
export type ImageAndroid = AbstractImageAndroid & ImageComponentStaticsAndroid;
export type AbstractImageIOS = component(
ref: React.RefSetter<React.ElementRef<ImageViewNativeComponent>>,
ref?: React.RefSetter<React.ElementRef<ImageViewNativeComponent>>,
...props: ImagePropsType
);
export type ImageIOS = AbstractImageIOS & ImageComponentStaticsIOS;
@@ -1344,8 +1344,8 @@ export class ImageTest extends React.Component {
const uri =
'https://seeklogo.com/images/T/typescript-logo-B29A3F462D-seeklogo.com.png';
const headers = {Authorization: 'Bearer test'};
const image: ImageResolvedAssetSource = Image.resolveAssetSource({uri});
console.log(image.width, image.height, image.scale, image.uri);
const image = Image.resolveAssetSource({uri});
console.log(image?.width, image?.height, image?.scale, image?.uri);
Image.queryCache &&
Image.queryCache([uri]).then(({[uri]: status}) => {
@@ -1380,14 +1380,14 @@ export class ImageTest extends React.Component {
Image.prefetch(uri); // $ExpectType Promise<boolean>
}
handleOnLoad = (e: NativeSyntheticEvent<ImageLoadEventData>) => {
handleOnLoad = (e: NativeSyntheticEvent<Readonly<ImageLoadEventData>>) => {
testNativeSyntheticEvent(e);
console.log('height:', e.nativeEvent.source.height);
console.log('width:', e.nativeEvent.source.width);
console.log('uri:', e.nativeEvent.source.uri);
};
handleOnError = (e: NativeSyntheticEvent<ImageErrorEventData>) => {
handleOnError = (e: NativeSyntheticEvent<Readonly<ImageErrorEventData>>) => {
testNativeSyntheticEvent(e);
console.log('error:', e.nativeEvent.error);
};
+2
View File
@@ -80,6 +80,8 @@ const ENTRY_POINTS = [
'packages/react-native/Libraries/Components/Switch/Switch.js',
'packages/react-native/Libraries/Components/StatusBar/StatusBar.js',
'packages/react-native/Libraries/Components/RefreshControl/RefreshControl.js',
'packages/react-native/Libraries/Image/Image.js.flow',
'packages/react-native/Libraries/Image/ImageBackground.js',
];
/**