@deboxsoft/react-native-elements
Version:
React Native Elements & UI Toolkit
296 lines (280 loc) • 6.98 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import {
View,
Text,
Image,
Platform,
StyleSheet,
TouchableOpacity,
TouchableHighlight,
TouchableNativeFeedback,
TouchableWithoutFeedback,
Animated,
} from 'react-native';
import { withTheme, ViewPropTypes } from '../config';
import { renderNode, nodeType } from '../helpers';
import Icon from '../icons/Icon';
const DEFAULT_COLORS = ['#000', '#333', '#555', '#888', '#aaa', '#ddd'];
const DEFAULT_SIZES = {
small: 34,
medium: 50,
large: 75,
xlarge: 150,
};
const Avatar = ({
onPress,
onLongPress,
Component = onPress || onLongPress ? TouchableOpacity : View,
containerStyle,
icon,
iconStyle,
source,
size,
avatarStyle,
rounded,
title,
titleStyle,
overlayContainerStyle,
showEditButton,
editButton,
onEditPress,
imageProps,
placeholderStyle,
renderPlaceholderContent,
ImageComponent,
...attributes
}) => {
const width =
typeof size === 'number'
? size
: DEFAULT_SIZES[size] || DEFAULT_SIZES.small;
const height = width;
const titleSize = width / 2;
const iconSize = width / 2;
const editButtonSize = editButton.size || (width + height) / 2 / 3;
const Utils = showEditButton && (
<TouchableHighlight
style={[
styles.editButton,
{
width: editButtonSize,
height: editButtonSize,
borderRadius: editButtonSize / 2,
},
editButton.style,
]}
underlayColor={editButton.underlayColor}
onPress={onEditPress}
>
<View>
<Icon
size={editButtonSize * 0.8}
name={editButton.iconName}
type={editButton.iconType}
color={editButton.iconColor}
/>
</View>
</TouchableHighlight>
);
const PlaceholderContent =
(renderPlaceholderContent &&
renderNode(undefined, renderPlaceholderContent)) ||
(title && (
<Text style={[styles.title, { fontSize: titleSize }, titleStyle]}>
{title}
</Text>
)) ||
(icon && (
<Icon
style={iconStyle && iconStyle}
color={icon.color || 'white'}
name={icon.name || 'user'}
size={icon.size || iconSize}
type={icon.type && icon.type}
/>
));
return (
<Component
onPress={onPress}
onLongPress={onLongPress}
style={[
styles.container,
{ height, width },
rounded && { borderRadius: width / 2, overflow: 'hidden' },
containerStyle,
]}
{...attributes}
>
<FadeInImage
placeholderStyle={placeholderStyle}
PlaceholderContent={PlaceholderContent}
containerStyle={overlayContainerStyle}
source={source}
{...imageProps}
style={[imageProps && imageProps.style, avatarStyle]}
ImageComponent={ImageComponent}
/>
{Utils}
</Component>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: 'transparent',
},
avatar: {
flex: 1,
width: null,
height: null,
},
overlayContainer: {
flex: 1,
},
title: {
color: '#ffffff',
backgroundColor: 'transparent',
textAlign: 'center',
},
editButton: {
position: 'absolute',
bottom: 0,
right: 0,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: DEFAULT_COLORS[4],
...Platform.select({
ios: {
shadowColor: DEFAULT_COLORS[0],
shadowOffset: { width: 1, height: 1 },
shadowRadius: 2,
shadowOpacity: 0.5,
},
android: {
elevation: 1,
},
}),
},
placeholderContainer: {
...StyleSheet.absoluteFillObject,
},
placeholder: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#BDBDBD',
},
});
Avatar.propTypes = {
Component: PropTypes.oneOf([
View,
TouchableOpacity,
TouchableHighlight,
TouchableNativeFeedback,
TouchableWithoutFeedback,
]),
onPress: PropTypes.func,
onLongPress: PropTypes.func,
containerStyle: ViewPropTypes.style,
source: Image.propTypes.source,
avatarStyle: PropTypes.any,
rounded: PropTypes.bool,
title: PropTypes.string,
titleStyle: Text.propTypes.style,
overlayContainerStyle: ViewPropTypes.style,
activeOpacity: PropTypes.number,
icon: PropTypes.object,
iconStyle: Text.propTypes.style,
size: PropTypes.oneOfType([
PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
PropTypes.number,
]),
showEditButton: PropTypes.bool,
onEditPress: PropTypes.func,
editButton: PropTypes.shape({
size: PropTypes.number,
iconName: PropTypes.string,
iconType: PropTypes.string,
iconColor: PropTypes.string,
underlayColor: PropTypes.string,
style: ViewPropTypes.style,
}),
placeholderStyle: ViewPropTypes.style,
renderPlaceholderContent: nodeType,
imageProps: PropTypes.object,
ImageComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.object,
])
};
Avatar.defaultProps = {
showEditButton: false,
onEditPress: null,
size: 'small',
editButton: {
size: null,
iconName: 'mode-edit',
iconType: 'material',
iconColor: '#fff',
underlayColor: DEFAULT_COLORS[0],
style: null,
},
ImageComponent: Image,
};
class FadeInImage extends React.PureComponent {
placeholderContainerOpacity = new Animated.Value(1);
onLoadEnd = () => {
/* Images finish loading in the same frame for some reason,
the images will fade in separately with staggerNonce */
const minimumWait = 100;
const staggerNonce = 200 * Math.random();
setTimeout(
() =>
Animated.timing(this.placeholderContainerOpacity, {
toValue: 0,
duration: 350,
useNativeDriver: true,
}).start(),
minimumWait + staggerNonce
);
};
render() {
const {
placeholderStyle,
PlaceholderContent,
containerStyle,
style,
ImageComponent,
...attributes
} = this.props;
return Platform.OS === 'ios' ? (
<View style={[styles.overlayContainer, containerStyle]}>
<ImageComponent
{...attributes}
onLoadEnd={this.onLoadEnd}
style={[styles.avatar, style]}
/>
<Animated.View
style={[
styles.placeholderContainer,
{ opacity: this.placeholderContainerOpacity },
]}
>
<View style={[style, styles.placeholder, placeholderStyle]}>
{PlaceholderContent}
</View>
</Animated.View>
</View>
) : (
<View style={[styles.overlayContainer, containerStyle]}>
<View style={styles.placeholderContainer}>
<View style={[style, styles.placeholder, placeholderStyle]}>
{PlaceholderContent}
</View>
</View>
<Image {...attributes} style={[styles.avatar, style]} />
</View>
);
}
}
export default withTheme(Avatar, 'Avatar');