azure-devops-ui
Version:
React components for building web UI in Azure DevOps
147 lines (146 loc) • 6.78 kB
JavaScript
import "../../CommonImports";
import "../../Core/core.css";
import "./VssPersona.css";
import * as React from "react";
import { Icon } from '../../Icon';
import { Tooltip } from '../../TooltipEx';
import { css, KeyCode } from '../../Util';
import { getColorString } from '../../Utilities/Color';
import { getInitialsColorFromName, getInitialsFromName } from "./VssPersona.Initials";
/**
* Renders a user's profile/identity/avatar image.
*/
export class VssPersona extends React.Component {
constructor(props) {
super(props);
this.setTargetElement = (element) => {
this.targetElement = element;
};
this.onImageError = (event) => {
if (this.props.showInitialsOnImageError) {
this.setState({ imageError: true });
}
else if (this.props.onImageError) {
this.props.onImageError(event);
}
};
this.handleKeyDown = (e) => {
if (e.keyCode === KeyCode.enter || e.keyCode === KeyCode.space) {
this.showPersonaCard();
}
};
this.showPersonaCard = () => {
if (!this.props.suppressPersonaCard) {
this.setState({ showPersonaCard: true });
}
};
this.hidePersonaCard = () => {
this.setState({ showPersonaCard: false });
};
this.state = {
imageError: false,
showPersonaCard: false,
imageUrlVal: this._getImageUrl(props)
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
showPersonaCard: false,
imageUrlVal: this._getImageUrl(nextProps)
});
}
shouldComponentUpdate(nextProps, nextState) {
if (!nextProps.identityDetailsProvider && (this.props.imageUrl !== nextProps.imageUrl || this.props.displayName !== nextProps.displayName)) {
return true;
}
else if (!this.props.identityDetailsProvider && !nextProps.identityDetailsProvider) {
return false;
}
else if (!this.props.identityDetailsProvider || !nextProps.identityDetailsProvider) {
return true;
}
return (this.props.size !== nextProps.size ||
this.props.cssClass !== nextProps.cssClass ||
this.props.identityDetailsProvider !== nextProps.identityDetailsProvider ||
this.state.showPersonaCard !== nextState.showPersonaCard);
}
render() {
const { ariaLabel, identityDetailsProvider, size = "medium", imgAltText = "", imageUrl, displayName } = this.props;
const imageUrlVal = !identityDetailsProvider ? imageUrl : this.state.imageUrlVal;
// Set the focus and aria-expand attributes based on props passed
const additionalAttributes = {};
additionalAttributes["role"] = "img";
if (this.props.dataIsFocusable) {
additionalAttributes["data-is-focusable"] = true;
}
if (this.props.isTabStop) {
additionalAttributes["tabIndex"] = 0;
}
// Setting the aria related properties and user action delegates unless we supress persona card or persona card isnt't provided
if (!this.props.suppressPersonaCard && this.props.identityDetailsProvider && this.props.identityDetailsProvider.onRenderPersonaCard) {
additionalAttributes["aria-expanded"] = this.state.showPersonaCard;
additionalAttributes["onKeyDown"] = this.handleKeyDown;
additionalAttributes["onClick"] = this.showPersonaCard;
additionalAttributes["role"] = "button";
}
const displayNameVal = !identityDetailsProvider ? displayName : identityDetailsProvider.getDisplayName();
if (ariaLabel) {
additionalAttributes["aria-label"] = ariaLabel;
}
else if (displayNameVal) {
additionalAttributes["aria-label"] = displayNameVal;
}
else {
additionalAttributes["aria-hidden"] = "true";
}
const backgroundColor = displayNameVal === undefined ? undefined : getInitialsColorFromName(displayNameVal);
const imageElement = imageUrlVal !== undefined && !this.state.imageError ? (React.createElement("img", { className: "vss-Persona-content using-image", src: imageUrlVal, alt: imgAltText, onError: this.onImageError })) : (React.createElement("div", { className: css("vss-Persona-content", size), style: backgroundColor && { background: getColorString(backgroundColor) } }, displayNameVal ? React.createElement("span", null, getInitialsFromName(displayNameVal)) : React.createElement(Icon, { iconName: "Contact" })));
// Getting the reference to the div around the image because the Callout within PersonaCard has positioning problems in some cases when passing in img element as the target
return (React.createElement(React.Fragment, null,
React.createElement(Tooltip, { text: displayNameVal, showOnFocus: true },
React.createElement("div", Object.assign({ className: css("vss-Persona flex-noshrink", this.props.className, this.props.cssClass, size), ref: this.setTargetElement }, additionalAttributes), imageElement)),
!this.props.suppressPersonaCard &&
this.state.showPersonaCard &&
identityDetailsProvider &&
identityDetailsProvider.onRenderPersonaCard &&
identityDetailsProvider.onRenderPersonaCard(this.targetElement, this.hidePersonaCard)));
}
/**
* Resolve the URL for the profile image.
* @param props
*/
_getImageUrl(props) {
const { identityDetailsProvider, size = "medium" } = props;
const sizePx = this._getSize(size);
return identityDetailsProvider && identityDetailsProvider.getIdentityImageUrl(sizePx);
}
/**
* Get the size in pixels for the given css class.
* @param size
*/
_getSize(size) {
switch (size) {
case "extra-extra-small":
return 16;
case "extra-small":
return 18;
case "extra-small-plus":
return 20;
case "small":
return 24;
case "small-plus":
return 28;
case "medium":
return 32;
case "medium-plus":
return 40;
default:
case "large":
return 48;
case "extra-large":
return 72;
case "extra-extra-large":
return 100;
}
}
}