create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
217 lines (197 loc) • 6.57 kB
Flow
/**
* Copyright (c) Nicolas Gallagher.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type { ImageProps } from './types';
import * as React from 'react';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import { getAssetByID } from '../../modules/AssetRegistry';
import resolveShadowValue from '../StyleSheet/resolveShadowValue';
import ImageLoader from '../../modules/ImageLoader';
import PixelRatio from '../PixelRatio';
import StyleSheet from '../StyleSheet';
import TextAncestorContext from '../Text/TextAncestorContext';
import View from '../View';
export type { ImageProps };
const ERRORED = 'ERRORED';
const LOADED = 'LOADED';
const LOADING = 'LOADING';
const IDLE = 'IDLE';
let _filterId = 0;
const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/;
declare function createTintColorSVG(tintColor: any, id: any): any;
declare function getFlatStyle(style: any, blurRadius: any, filterId: any): any;
declare function resolveAssetDimensions(source: any): any;
declare function resolveAssetUri(source: any): ?string;
interface ImageStatics {
getSize: (uri: string, success: (width: number, height: number) => void, failure: () => void) => void,
prefetch: (uri: string) => Promise<void>,
queryCache: (uris: Array<string>) => Promise<{|
[uri: string]: 'disk/memory'
|}>,
}
const Image: React.AbstractComponent<ImageProps, React.ElementRef<typeof View>> = React.forwardRef((props, ref) => {
const {
accessibilityLabel,
blurRadius,
defaultSource,
draggable,
onError,
onLayout,
onLoad,
onLoadEnd,
onLoadStart,
pointerEvents,
source,
style,
...rest
} = props;
if (process.env.NODE_ENV !== 'production') {
if (props.children) {
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.');
}
}
const [state, updateState] = React.useState(() => {
const uri = resolveAssetUri(source);
if (uri != null) {
const isLoaded = ImageLoader.has(uri);
if (isLoaded) {
return LOADED;
}
}
return IDLE;
});
const [layout, updateLayout] = React.useState({});
const hasTextAncestor = React.useContext(TextAncestorContext);
const hiddenImageRef = React.useRef(null);
const filterRef = React.useRef(_filterId++);
const requestRef = React.useRef(null);
const shouldDisplaySource = state === LOADED || state === LOADING && defaultSource == null;
const [flatStyle, _resizeMode, filter, tintColor] = getFlatStyle(style, blurRadius, filterRef.current);
const resizeMode = props.resizeMode || _resizeMode || 'cover';
const selectedSource = shouldDisplaySource ? source : defaultSource;
const displayImageUri = resolveAssetUri(selectedSource);
const imageSizeStyle = resolveAssetDimensions(selectedSource);
const backgroundImage = displayImageUri ? `url("${displayImageUri}")` : null;
const backgroundSize = getBackgroundSize(); // Accessibility image allows users to trigger the browser's image context menu
const hiddenImage = displayImageUri ? createElement('img', {
alt: accessibilityLabel || '',
classList: [classes.accessibilityImage],
draggable: draggable || false,
ref: hiddenImageRef,
src: displayImageUri
}) : null;
declare function getBackgroundSize(): ?string;
declare function handleLayout(e: any): any; // Image loading
const uri = resolveAssetUri(source);
React.useEffect(() => {
abortPendingRequest();
if (uri != null) {
updateState(LOADING);
if (onLoadStart) {
onLoadStart();
}
requestRef.current = ImageLoader.load(uri, function load(e) {
updateState(LOADED);
if (onLoad) {
onLoad(e);
}
if (onLoadEnd) {
onLoadEnd();
}
}, function error() {
updateState(ERRORED);
if (onError) {
onError({
nativeEvent: {
error: `Failed to load resource ${uri} (404)`
}
});
}
if (onLoadEnd) {
onLoadEnd();
}
});
}
declare function abortPendingRequest(): any;
return abortPendingRequest;
}, [uri, requestRef, updateState, onError, onLoad, onLoadEnd, onLoadStart]);
return <View {...rest} accessibilityLabel={accessibilityLabel} onLayout={handleLayout} pointerEvents={pointerEvents} ref={ref} style={[styles.root, hasTextAncestor && styles.inline, imageSizeStyle, flatStyle]}>
<View style={[styles.image, resizeModeStyles[resizeMode], {
backgroundImage,
filter
}, backgroundSize != null && {
backgroundSize
}]} suppressHydrationWarning={true} />
{hiddenImage}
{createTintColorSVG(tintColor, filterRef.current)}
</View>;
});
Image.displayName = 'Image'; // $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet
const ImageWithStatics = (Image: React.AbstractComponent<ImageProps, React.ElementRef<typeof View>> & ImageStatics);
ImageWithStatics.getSize = function (uri, success, failure) {
ImageLoader.getSize(uri, success, failure);
};
ImageWithStatics.prefetch = function (uri) {
return ImageLoader.prefetch(uri);
};
ImageWithStatics.queryCache = function (uris) {
return ImageLoader.queryCache(uris);
};
const classes = css.create({
accessibilityImage: { ...StyleSheet.absoluteFillObject,
height: '100%',
opacity: 0,
width: '100%',
zIndex: -1
}
});
const styles = StyleSheet.create({
root: {
flexBasis: 'auto',
overflow: 'hidden',
zIndex: 0
},
inline: {
display: 'inline-flex'
},
image: { ...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
height: '100%',
width: '100%',
zIndex: -1
}
});
const resizeModeStyles = StyleSheet.create({
center: {
backgroundSize: 'auto'
},
contain: {
backgroundSize: 'contain'
},
cover: {
backgroundSize: 'cover'
},
none: {
backgroundPosition: '0 0',
backgroundSize: 'auto'
},
repeat: {
backgroundPosition: '0 0',
backgroundRepeat: 'repeat',
backgroundSize: 'auto'
},
stretch: {
backgroundSize: '100% 100%'
}
});
export default ImageWithStatics;