react-native-ui-lib
Version:
<p align="center"> <img src="https://user-images.githubusercontent.com/1780255/105469025-56759000-5ca0-11eb-993d-3568c1fd54f4.png" height="250px" style="display:block"/> </p> <p align="center">UI Toolset & Components Library for React Native</p> <p a
257 lines (228 loc) • 6.15 kB
JavaScript
import _pt from "prop-types";
import _ from 'lodash';
import React, { PureComponent } from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';
import { StyleSheet, Image as RNImage, ImageBackground } from 'react-native';
import { Constants, asBaseComponent } from "../../commons/new";
import { getAsset, isSvg } from "../../utils/imageUtils";
import Overlay from "../overlay";
import SvgImage from "../svgImage";
import View from "../view";
import { Colors } from "../../style";
/**
* @description: Image wrapper with extra functionality like source transform and assets support
* @extends: Image
* @extendsLink: https://reactnative.dev/docs/image
* @notes: please note that for SVG support you need to add both
* `react-native-svg` and `react-native-svg-transformer`,
* and also configure them (see `metro.config.js`)
*/
class Image extends PureComponent {
static propTypes = {
/**
* custom source transform handler for manipulating the image source (great for size control)
*/
sourceTransformer: _pt.func,
/**
* if provided image source will be driven from asset name
*/
assetName: _pt.string,
/**
* the asset group, default is "icons"
*/
assetGroup: _pt.string,
/**
* the asset tint
*/
tintColor: _pt.string,
/**
* whether the image should flip horizontally on RTL locals
*/
supportRTL: _pt.bool,
/**
* Show image as a cover, full width, image (according to aspect ratio, default: 16:8)
*/
cover: _pt.bool,
/**
* The aspect ratio for the image
*/
aspectRatio: _pt.number,
/**
* Pass a custom color for the overlay
*/
overlayColor: _pt.string,
/**
* Render an overlay with custom content
*/
customOverlayContent: _pt.element
};
static displayName = 'Image';
static defaultProps = {
assetGroup: 'icons'
};
static overlayTypes = Overlay.overlayTypes;
static overlayIntensityType = Overlay.intensityTypes;
constructor(props) {
super(props);
this.sourceTransformer = this.props.sourceTransformer;
this.state = {
error: false,
prevSource: props.source
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.source !== prevState.prevSource) {
return {
error: false,
prevSource: nextProps.source
};
}
return null;
}
isGif() {
if (Constants.isAndroid) {
const {
source
} = this.props;
const url = _.get(source, 'uri');
const isGif = /(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|gif|png)/.test(url);
return isGif;
}
}
shouldUseImageBackground() {
const {
overlayType,
customOverlayContent
} = this.props;
return !!overlayType || this.isGif() || !_.isUndefined(customOverlayContent);
}
getVerifiedSource(source) {
if (_.get(source, 'uri') === null || _.get(source, 'uri') === '') {
// @ts-ignore
return { ...source,
uri: undefined
};
}
return source;
}
getImageSource() {
const {
assetName,
assetGroup,
source
} = this.props;
if (!_.isUndefined(assetName)) {
return getAsset(assetName, assetGroup);
}
if (this.sourceTransformer) {
return this.sourceTransformer(this.props);
}
return this.getVerifiedSource(source);
}
onError = event => {
if (event.nativeEvent.error) {
this.setState({
error: true
});
this.props.onError?.(event);
}
};
renderSvg = () => {
const {
source,
...others
} = this.props;
return <SvgImage data={source} {...others} />;
};
renderErrorImage = () => {
const {
style,
cover,
modifiers
} = this.props;
const {
margins
} = modifiers;
return <View style={[margins, style, styles.errorImageContainer, cover && styles.coverImage]}>
{this.renderImage(true)}
</View>;
};
renderImage = useImageInsideContainer => {
const {
error
} = this.state;
const source = error ? this.getVerifiedSource(this.props.errorSource) : this.getImageSource();
const {
tintColor,
style,
supportRTL,
cover,
aspectRatio,
overlayType,
overlayIntensity,
overlayColor,
customOverlayContent,
modifiers,
...others
} = this.props;
const shouldFlipRTL = supportRTL && Constants.isRTL;
const ImageView = this.shouldUseImageBackground() ? ImageBackground : RNImage;
const {
margins
} = modifiers;
const resizeMode = useImageInsideContainer ? 'contain' : undefined;
return (// @ts-ignore
<ImageView style={[tintColor && {
tintColor
}, shouldFlipRTL && styles.rtlFlipped, cover && styles.coverImage, this.isGif() && styles.gifImage, aspectRatio && {
aspectRatio
}, !useImageInsideContainer && margins, style, useImageInsideContainer && styles.shrink]} resizeMode={resizeMode} accessible={false} accessibilityRole={'image'} {...others} onError={this.onError} source={source}>
{(overlayType || customOverlayContent) && <Overlay type={overlayType} intensity={overlayIntensity} color={overlayColor} customContent={customOverlayContent} />}
</ImageView>
);
};
renderRegularImage() {
const {
error
} = this.state;
if (error) {
return this.renderErrorImage();
} else {
return this.renderImage(false);
}
}
render() {
const {
source
} = this.props;
if (isSvg(source)) {
return this.renderSvg();
} else {
return this.renderRegularImage();
}
}
}
const styles = StyleSheet.create({
rtlFlipped: {
transform: [{
scaleX: -1
}]
},
coverImage: {
width: '100%',
aspectRatio: 16 / 8
},
gifImage: {
overflow: 'hidden'
},
errorImageContainer: {
backgroundColor: Colors.grey70,
zIndex: -1
},
shrink: {
flexShrink: 1
}
});
hoistNonReactStatic(Image, RNImage);
export { Image };
export default asBaseComponent(Image);