UNPKG

create-expo-cljs-app

Version:

Create a react native application with Expo and Shadow-CLJS!

217 lines (197 loc) 6.57 kB
/** * 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;