UNPKG

wix-style-react

Version:
239 lines (225 loc) • 8.25 kB
import React from 'react'; import PropTypes from 'prop-types'; import IconButton from '../IconButton'; import { avatarShapes, dataHooks } from './constants'; import { Avatar as CoreAvatar } from 'wix-ui-core/dist/src/components/avatar'; import Loader from '../Loader'; import { placeholderSVGs } from './assets'; import { st, classes } from './Avatar.st.css'; import { capitalize } from '../utils/cssClassUtils'; import stringToColor from './string-to-color'; import { FontUpgradeContext } from '../FontUpgrade/context'; const getSizeNumber = size => Number(size.substring(4)); const defaultSize = 48; const minSmallIconButton = 60; /** * Avatar is a type of element that visually represents a user, either as an image, name initials or placeholder icon. */ class Avatar extends React.PureComponent { constructor(props) { super(props); this.state = { fadeIndication: false, showIndication: false, }; } _onMouseEnter = () => { if (this.props.showIndicationOnHover) { this.setState({ showIndication: true }); } }; _onMouseLeave = () => { if (this.props.showIndicationOnHover) { this.setState({ fadeIndication: true }); setTimeout( () => this.setState({ fadeIndication: false, showIndication: false }), 150, ); } }; render() { const { size, presence, indication, color, customIndication, onIndicationClick, dataHook, className, shape, text, placeholder, name, onClick, showIndicationOnHover, loading, ...rest } = this.props; const { fadeIndication, showIndication } = this.state; const calculatedColor = color || stringToColor(text || name); // if color is provided as a prop use it, otherwise, generate a color based on the text const sizeNumber = getSizeNumber(size); const renderOnHover = !showIndicationOnHover || showIndication; const indicationConstraints = renderOnHover && sizeNumber >= defaultSize; const renderIndication = indicationConstraints && !customIndication && indication; const renderCustomIndication = indicationConstraints && customIndication; const renderLoader = loading && sizeNumber >= defaultSize; return ( <div data-hook={dataHook} className={st(className, classes.root)}> <FontUpgradeContext.Consumer> {({ active }) => ( <div data-hook={dataHooks.avatarWSR} onMouseEnter={this._onMouseEnter} onMouseLeave={this._onMouseLeave} className={st(classes.avatarContainer, { shape, size, indication: Boolean(customIndication || indication), presence: Boolean(presence), presenceType: presence, clickable: !!onClick, fade: fadeIndication, hasText: !!text, })} data-madefor={active} > <div className={classes.coreAvatar}> <CoreAvatar {...{ ...rest, placeholder: placeholder ? ( placeholder ) : ( <AvatarDefaultPlaceholder shape={shape} size={size} /> ), text, name, onClick, initialsLimit: sizeNumber < 30 ? 1 : undefined, 'data-hook': dataHooks.avatarCore, }} className={st( classes.avatar, classes[`color${capitalize(calculatedColor)}`], )} /> </div> {renderLoader && [ <div key="overlay" className={st(classes.loaderContainer, classes.overlay)} />, <div key="loader" className={st(classes.loaderContainer, classes.loader)} > <Loader dataHook={dataHooks.loader} size="tiny" /> </div>, ]} {presence && <div className={classes.presence} />} {renderIndication && ( <div className={classes.indication}> <IconButton className={classes.iconButtonShadow} dataHook={dataHooks.indication} onClick={onIndicationClick} skin="inverted" shape={shape} size={sizeNumber > minSmallIconButton ? 'small' : 'tiny'} > {indication} </IconButton> </div> )} {renderCustomIndication && ( <div className={classes.indication} data-hook={dataHooks.customIndication} onClick={onIndicationClick} > {customIndication} </div> )} </div> )} </FontUpgradeContext.Consumer> </div> ); } } const AvatarDefaultPlaceholder = ({ shape, size }) => shape !== avatarShapes.square ? placeholderSVGs[size][avatarShapes.circle] : placeholderSVGs[size][avatarShapes.square]; const CoreAvatarPropTypes = { /** * Defines a name of the avatar user. Text initials will be generated from the name. * The name value will be used as default value for HTML title and `aria-label` attributes. * And also as default value for the image's alt attribute if `imgProps` is provided. */ name: PropTypes.string, /** Defines a text to render as a content instead of a given `name`. */ text: PropTypes.string, /** * Accept any content to render as a content placeholder. This placeholder will be displayed if no text, name or imgProps are provided.<br> * By default use a generic avatar user icon. */ placeholder: PropTypes.node, /** Accept all common `<img>` tag properties. */ imgProps: PropTypes.object, /** Defines a string that labels the current element in case where text label is not visible on the screen. */ ariaLabel: PropTypes.string, /** Defines a standard HTML title attribute value. Applies it to the root element. */ title: PropTypes.string, /** Defines a click event handler. When defined, component will be clickable and will have a pointer cursor on hover. */ onClick: PropTypes.func, }; Avatar.displayName = 'Avatar'; Avatar.propTypes = { ...CoreAvatarPropTypes, /** Controls the size of an avatar. */ size: PropTypes.oneOf([ 'size90', 'size72', 'size60', 'size48', 'size36', 'size30', 'size24', 'size18', ]), /** * Controls the background color of the avatar. If not provided, * color is determined by the provided text or name so that each name will receive a different color. */ color: PropTypes.oneOf(['A1', 'A2', 'A3', 'A4', 'A5', 'A6']), /** Controls the shape of the image. */ shape: PropTypes.oneOf(['circle', 'square']), /** Specifies a CSS class name to be appended to the component’s root element. */ className: PropTypes.string, /** Applies a data-hook HTML attribute that can be used in the tests. */ dataHook: PropTypes.string, /** Sets the presence status of an avatar. */ presence: PropTypes.oneOf(['online', 'offline', 'busy']), /** Accept any content to render as an indication item. */ indication: PropTypes.node, /** * Accept any content to render as a custom indication item. This indication element will not be wrapped by an IconButton. * It could be rendered in other shapes (such as square). * */ customIndication: PropTypes.node, /** Defines a callback function which is called every time indication element is clicked. */ onIndicationClick: PropTypes.func, /** Shows indication element on hover. */ showIndicationOnHover: PropTypes.bool, /** Shows a loader on top of an avatar. */ loading: PropTypes.bool, }; Avatar.defaultProps = { size: 'size48', shape: 'circle', showIndicationOnHover: false, }; export default Avatar;