UNPKG

wix-style-react

Version:
146 lines 6 kB
import * as React from 'react'; import classNames from 'classnames'; import { st, classes } from './AvatarCore.st.css'; import { filterDataProps } from '../../utils/propsUtils'; import { withFocusable } from '../../common/Focusable'; import { nameToInitials } from './utils'; const DEFAULT_CONTENT_TYPE = 'placeholder'; /** * AvatarCore is a type of element that visually represents a user, either as an image, placeholder or text. * * <p>There are 3 props for corresponding content types: `text`, `placeholder` and `imgProps`. * If more than one of these props is supplied (with `name` prop giving default value to the `text` prop), * then the resolved content type for display goes according to this priority: image -> text -> placeholder. */ class AvatarCore extends React.Component { constructor() { super(...arguments); this.state = { imgLoaded: false }; this.img = undefined; this.loadImg = () => { this.img = new Image(); this.img.onload = () => { // don't set state after unmount this.img && this.setState({ imgLoaded: true }); }; this.img.src = this.props.imgProps?.src ?? ''; }; this.unloadImg = () => { // TODO: Is this necessary? It is taken from https://github.com/mbrevda/react-image/blob/c402ed3f5d54b88e51eca3326a1e81d964995795/src/index.js#L146 // @ts-ignore delete this.img.onload; try { // @ts-ignore delete this.img.src; } catch (e) { // On Safari in Strict mode this will throw an exception, // - https://github.com/mbrevda/react-image/issues/187 // We don't need to do anything about it. } // @ts-ignore delete this.img; }; this._handleKeyDown = event => { if (event.key === ' ' || event.key === 'Enter' || event.key === 'Space') { event.preventDefault(); // @ts-ignore this.props.onClick(); } }; } /** This is the resolved content type the consumer wants to display */ getRequestedContentType(props) { const { name, text, placeholder, imgProps } = props; return imgProps && imgProps.src ? 'image' : text || name ? 'text' : placeholder ? 'placeholder' : DEFAULT_CONTENT_TYPE; } /** This is content type that will be displayed. (If img is loading then this will be the fallback) */ getCurrentContentType() { const requestedType = this.getRequestedContentType(this.props); if (requestedType === 'image' && !this.state.imgLoaded) { const { name, text, placeholder } = this.props; return text || name ? 'text' : placeholder ? 'placeholder' : DEFAULT_CONTENT_TYPE; } return requestedType; } componentDidMount() { this.getRequestedContentType(this.props) === 'image' && !this.state.imgLoaded && this.loadImg(); } UNSAFE_componentWillReceiveProps(nextProps) { if (!nextProps.imgProps || !this.props.imgProps || nextProps.imgProps.src !== this.props.imgProps.src) { this.setState({ imgLoaded: false }); this.img && this.unloadImg(); } } componentDidUpdate() { this.getRequestedContentType(this.props) === 'image' && !this.img && !this.state.imgLoaded && this.loadImg(); } componentWillUnmount() { this.img && this.unloadImg(); } getContent(contentType) { switch (contentType) { case 'text': { const { name, text } = this.props; const textContent = text || nameToInitials(name, this.props.initialsLimit); return (React.createElement("div", { className: classes.content, "data-hook": "text-container" }, textContent)); } case 'placeholder': { const { placeholder } = this.props; return !placeholder ? null : React.cloneElement(placeholder, { className: classNames(placeholder.props.className, classes.content), }); } case 'image': { if (!this.props.imgProps) { return null; } const { alt, className, ...rest } = this.props.imgProps; return (React.createElement("img", { "data-hook": "content", className: classNames(classes.content, className), alt: alt ? alt : this.props.name, ...rest })); } default: { return null; } } } render() { const { name, title, ariaLabel, onClick, focusableOnFocus, focusableOnBlur, } = this.props; const contentType = this.getCurrentContentType(); const focusProps = !!onClick && { role: 'button', onFocus: focusableOnFocus, onBlur: focusableOnBlur, onKeyDown: this._handleKeyDown, tabIndex: 0, }; return (React.createElement("div", { "data-content-type": contentType, "data-img-loaded": this.state.imgLoaded, title: title || name, "aria-label": ariaLabel || name, onClick: onClick, ...focusProps, className: st(classes.root, { imgLoaded: this.state.imgLoaded, contentType, }, this.props.className), ...filterDataProps(this.props) }, this.getContent(contentType))); } } AvatarCore.displayName = 'AvatarCore'; AvatarCore.defaultProps = { placeholder: undefined, }; export default withFocusable(AvatarCore); //# sourceMappingURL=AvatarCore.js.map