UNPKG

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

365 lines (315 loc) • 9.47 kB
import _pt from "prop-types"; import _ from 'lodash'; import React, { PureComponent } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import memoize from 'memoize-one'; import { LogService } from "../../services"; import { Colors } from "../../style"; import { forwardRef, asBaseComponent } from "../../commons/new"; import { extractAccessibilityProps } from "../../commons/modifiers"; import Badge from "../badge"; import View from "../view"; import Text from "../text"; import Image from "../image"; // @ts-ignore import AnimatedImage from "../animatedImage"; import * as AvatarHelper from "../../helpers/AvatarHelper"; export let BadgePosition; (function (BadgePosition) { BadgePosition["TOP_RIGHT"] = "TOP_RIGHT"; BadgePosition["TOP_LEFT"] = "TOP_LEFT"; BadgePosition["BOTTOM_RIGHT"] = "BOTTOM_RIGHT"; BadgePosition["BOTTOM_LEFT"] = "BOTTOM_LEFT"; })(BadgePosition || (BadgePosition = {})); const DEFAULT_BADGE_SIZE = 10; /** * @description: Avatar component for displaying user profile images * @extends: TouchableOpacity, Image * @image: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Avatar/Avarat_1.png?raw=true, https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Avatar/Avarat_2.png?raw=true * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/AvatarsScreen.tsx */ class Avatar extends PureComponent { static propTypes = { /** * Adds fade in animation when Avatar image loads */ animate: _pt.bool, /** * Background color for Avatar */ backgroundColor: _pt.string, /** * Badge location on Avatar */ badgePosition: _pt.oneOf(["TOP_RIGHT", "TOP_LEFT", "BOTTOM_RIGHT", "BOTTOM_LEFT"]), /** * The name of the avatar user. * If no label is provided, the initials will be generated from the name. * autoColorsConfig will use the name to create the background color of the Avatar. */ name: _pt.string, /** * Hash the name (or label) to get a color, so each name will have a specific color. * Default is false. */ useAutoColors: _pt.bool, /** * Send this to use the name to infer a backgroundColor */ autoColorsConfig: _pt.shape({ /** * Avatar colors to be used when useAutoColors is true */ avatarColors: _pt.arrayOf(_pt.string), /** * Replace the default hashing function (name -> number) */ hashFunction: _pt.func, /** * Background color in cases where the getBackgroundColor returns undefined. */ defaultColor: _pt.string }), /** * Label that can represent initials */ label: _pt.string, /** * The label color */ labelColor: _pt.string, /** * ribbon label to display on the avatar */ ribbonLabel: _pt.string, /** * Custom ribbon */ customRibbon: _pt.element, /** * Custom size for the Avatar */ size: _pt.number, /** * Press handler */ onPress: _pt.func, /** * Used as a testing identifier */ testID: _pt.string }; static displayName = 'Avatar'; constructor(props) { super(props); this.styles = createStyles(props); if (props.imageSource) { LogService.warn('uilib: imageSource prop is deprecated, use source instead.'); } } static defaultProps = { animate: false, size: 50, labelColor: Colors.grey10, badgePosition: BadgePosition.TOP_RIGHT }; static badgePosition = BadgePosition; get source() { return this.props.source || this.props.imageSource; } getContainerStyle() { const { size } = this.props; return { width: size, height: size, alignItems: 'center', justifyContent: 'center', borderRadius: size / 2 }; } getInitialsContainer() { const { size } = this.props; return { ...StyleSheet.absoluteFillObject, alignItems: 'center', justifyContent: 'center', borderRadius: size / 2 }; } getRibbonStyle() { const { size } = this.props; return { position: 'absolute', top: '10%', left: size / 1.7, borderRadius: size / 2 }; } getBadgeBorderWidth = () => _.get(this.props, 'badgeProps.borderWidth', 0); getBadgeColor() { return _.get(this.props, 'badgeProps.backgroundColor'); } getBadgeSize = () => { return this.props?.badgeProps?.size || DEFAULT_BADGE_SIZE; }; getBadgePosition = () => { const { size, badgePosition } = this.props; const radius = size / 2; const x = Math.sqrt(radius ** 2 * 2); const y = x - radius; const shift = Math.sqrt(y ** 2 / 2) - (this.getBadgeSize() + this.getBadgeBorderWidth() * 2) / 2; const badgeLocation = _.split(_.toLower(badgePosition), '_', 2); const badgeAlignment = { position: 'absolute', [badgeLocation[0]]: shift, [badgeLocation[1]]: shift }; return badgeAlignment; }; renderBadge() { const { testID, badgeProps } = this.props; if (badgeProps || this.getBadgeColor()) { return <Badge backgroundColor={this.getBadgeColor()} size={this.getBadgeSize()} {...badgeProps} containerStyle={this.getBadgePosition()} testID={`${testID}.onlineBadge`} />; } } renderRibbon() { const { ribbonLabel, ribbonStyle, ribbonLabelStyle, customRibbon } = this.props; if (ribbonLabel) { return customRibbon ? <View style={this.getRibbonStyle()}>{customRibbon}</View> : <View style={[this.getRibbonStyle(), this.styles.ribbon, ribbonStyle]}> <Text numberOfLines={1} text100 white style={[ribbonLabelStyle]}> {ribbonLabel} </Text> </View>; } } renderImage() { const { animate, // @ts-ignore onImageLoadStart, onImageLoadEnd, onImageLoadError, testID, imageProps, imageStyle } = this.props; const hasImage = !_.isUndefined(this.source); const ImageContainer = animate ? AnimatedImage : Image; if (hasImage) { return <ImageContainer animate={animate} style={[this.getContainerStyle(), StyleSheet.absoluteFillObject, imageStyle]} source={this.source} onLoadStart={onImageLoadStart} onLoadEnd={onImageLoadEnd} onError={onImageLoadError} testID={`${testID}.image`} containerStyle={this.getContainerStyle()} {...imageProps} />; } } getText = memoize((label, name) => { let text = label; if (_.isNil(label) && !_.isNil(name)) { text = AvatarHelper.getInitials(name); } return text; }); get text() { const { label, name } = this.props; return this.getText(label, name); } getBackgroundColor = memoize((text, avatarColors, hashFunction, defaultColor) => { return AvatarHelper.getBackgroundColor(text, avatarColors, hashFunction, defaultColor); }); get backgroundColor() { const { backgroundColor, useAutoColors, autoColorsConfig, name } = this.props; if (backgroundColor) { return backgroundColor; } const { avatarColors = AvatarHelper.getAvatarColors(), hashFunction = AvatarHelper.hashStringToNumber, defaultColor = Colors.grey80 } = autoColorsConfig || {}; if (useAutoColors) { return this.getBackgroundColor(name, avatarColors, hashFunction, defaultColor); } else { return defaultColor; } } render() { const { labelColor: color, onPress, containerStyle, children, size, testID, //@ts-ignore forwardedRef } = this.props; const Container = onPress ? TouchableOpacity : View; const hasImage = !_.isUndefined(this.source); const fontSizeToImageSizeRatio = 0.32; const fontSize = size * fontSizeToImageSizeRatio; const text = this.text; return <Container style={[this.getContainerStyle(), containerStyle]} ref={forwardedRef} testID={testID} onPress={onPress} accessible={!_.isUndefined(onPress)} accessibilityLabel={'Avatar'} accessibilityRole={onPress ? 'button' : 'image'} {...extractAccessibilityProps(this.props)}> <View testID={`${testID}.container`} style={[this.getInitialsContainer(), { backgroundColor: this.backgroundColor }, hasImage && this.styles.initialsContainerWithInset]}> {!_.isUndefined(text) && <Text numberOfLines={1} style={[{ fontSize }, this.styles.initials, { color }]} testID={`${testID}.label`}> {text} </Text>} </View> {this.renderImage()} {this.renderBadge()} {this.renderRibbon()} {children} </Container>; } } function createStyles(props) { const { labelColor } = props; const styles = StyleSheet.create({ initialsContainerWithInset: { top: 1, right: 1, bottom: 1, left: 1 }, initials: { color: labelColor, backgroundColor: 'transparent', lineHeight: undefined }, ribbon: { backgroundColor: Colors.primary, paddingHorizontal: 6, paddingVertical: 3 } }); return styles; } export { Avatar }; // For tests export default asBaseComponent(forwardRef(Avatar));