wix-style-react
Version:
wix-style-react
146 lines • 6 kB
JavaScript
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