@microsoft/mgt
Version:
The Microsoft Graph Toolkit
455 lines • 17.2 kB
JavaScript
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { customElement, html, property } from 'lit-element';
import { classMap } from 'lit-html/directives/class-map';
import { Providers } from '../../Providers';
import { ProviderState } from '../../providers/IProvider';
import '../../styles/fabric-icon-font';
import { getEmailFromGraphEntity } from '../../utils/GraphHelpers';
import '../sub-components/mgt-flyout/mgt-flyout';
import { MgtTemplatedComponent } from '../templatedComponent';
import { PersonCardInteraction } from './../PersonCardInteraction';
import { styles } from './mgt-person-css';
/**
* The person component is used to display a person or contact by using their photo, name, and/or email address.
*
* @export
* @class MgtPerson
* @extends {MgtTemplatedComponent}
*/
let MgtPerson = class MgtPerson extends MgtTemplatedComponent {
constructor() {
super();
/**
* Sets how the person-card is invoked
* Set to PersonCardInteraction.none to not show the card
*
* @type {PersonCardInteraction}
* @memberof MgtPerson
*/
this.personCardInteraction = PersonCardInteraction.none;
this.isPersonCardVisible = false;
this.personCardShouldRender = false;
this.handleWindowClick = this.handleWindowClick.bind(this);
}
/**
* Array of styles to apply to the element. The styles should be defined
* user the `css` tag function.
*/
static get styles() {
return styles;
}
/**
* Synchronizes property values when attributes change.
*
* @param {*} name
* @param {*} oldValue
* @param {*} newValue
* @memberof MgtPerson
*/
attributeChangedCallback(name, oldval, newval) {
super.attributeChangedCallback(name, oldval, newval);
if ((name === 'person-query' || name === 'user-id') && oldval !== newval) {
this.personDetails = null;
this.loadData();
}
}
/**
* Invoked when the element is first updated. Implement to perform one time
* work on the element after update.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
firstUpdated() {
Providers.onProviderUpdated(() => this.loadData());
this.loadData();
}
/**
* Invoked each time the custom element is appended into a document-connected element
*
* @memberof MgtPerson
*/
connectedCallback() {
super.connectedCallback();
window.addEventListener('click', this.handleWindowClick);
}
/**
* Invoked each time the custom element is disconnected from the document's DOM
*
* @memberof MgtPerson
*/
disconnectedCallback() {
window.removeEventListener('click', this.handleWindowClick);
super.disconnectedCallback();
}
/**
* Invoked on each update to perform rendering tasks. This method must return
* a lit-html TemplateResult. Setting properties inside this method will *not*
* trigger the element to update.
*/
render() {
const image = this.getImage();
const person = this.renderTemplate('default', { person: this.personDetails, personImage: image }) ||
html `
<div class="person-root">
${this.renderImage(image)} ${this.renderDetails()}
</div>
`;
return html `
<div
class="root"
=${this.handleMouseClick}
=${this.handleMouseEnter}
=${this.handleMouseLeave}
>
${this.renderFlyout(person)}
</div>
`;
}
/**
* Invoked whenever the element is updated. Implement to perform
* post-updating tasks via DOM APIs, for example, focusing an element.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param changedProperties Map of changed properties with old values
*/
updated(changedProps) {
super.updated(changedProps);
const initials = this.renderRoot.querySelector('.initials-text');
if (initials && initials.parentNode && initials.parentNode.getBoundingClientRect) {
const parent = initials.parentNode;
const height = parent.getBoundingClientRect().height;
initials.style.fontSize = `${height * 0.5}px`;
}
}
handleWindowClick(e) {
if (this.isPersonCardVisible && e.target !== this) {
this.hidePersonCard();
}
}
loadData() {
return __awaiter(this, void 0, void 0, function* () {
const provider = Providers.globalProvider;
if (!provider || provider.state === ProviderState.Loading) {
return;
}
if (provider.state === ProviderState.SignedOut) {
this.personDetails = null;
return;
}
// personDetails.personImage is a toolkit injected property to pass image between components
// an optimization to avoid fetching the image when unnecessary
if (this.personDetails) {
// in some cases we might only have name or email, but need to find the image
// use @ for the image value to search for an image
if (this.personImage && this.personImage === '@' && !this.personDetails.personImage) {
this.loadImage();
}
return;
}
if (this.userId || (this.personQuery && this.personQuery === 'me')) {
const batch = provider.graph.createBatch();
if (this.userId) {
batch.get('user', `/users/${this.userId}`, ['user.readbasic.all']);
batch.get('photo', `users/${this.userId}/photo/$value`, ['user.readbasic.all']);
}
else {
batch.get('user', 'me', ['user.read']);
batch.get('photo', 'me/photo/$value', ['user.read']);
}
const response = yield batch.execute();
this.personDetails = response.user;
this.personImage = response.photo;
this.personDetails.personImage = response.photo;
}
else if (!this.personDetails && this.personQuery) {
const people = yield provider.graph.findPerson(this.personQuery);
if (people && people.length > 0) {
const person = people[0];
this.personDetails = person;
this.loadImage();
}
}
});
}
loadImage() {
return __awaiter(this, void 0, void 0, function* () {
const provider = Providers.globalProvider;
const person = this.personDetails;
let image;
if (person.userPrincipalName) {
const userPrincipalName = person.userPrincipalName;
image = yield provider.graph.getUserPhoto(userPrincipalName);
}
else {
const email = getEmailFromGraphEntity(person);
if (email) {
// try to find a user by e-mail
const users = yield provider.graph.findUserByEmail(email);
if (users && users.length) {
if (users[0].personType && users[0].personType.subclass === 'OrganizationUser') {
image = yield provider.graph.getUserPhoto(users[0].scoredEmailAddresses[0].address);
}
else {
const contactId = users[0].id;
image = yield provider.graph.getContactPhoto(contactId);
}
}
}
}
if (image) {
this.personImage = image;
this.personDetails.personImage = image;
}
this.requestUpdate();
});
}
handleMouseClick(e) {
if (this.personCardInteraction !== PersonCardInteraction.none && !this.isPersonCardVisible) {
this.showPersonCard();
}
}
handleMouseEnter(e) {
clearTimeout(this._mouseEnterTimeout);
clearTimeout(this._mouseLeaveTimeout);
if (this.personCardInteraction !== PersonCardInteraction.hover) {
return;
}
this._mouseEnterTimeout = setTimeout(this.showPersonCard.bind(this), 500);
}
handleMouseLeave(e) {
clearTimeout(this._mouseEnterTimeout);
clearTimeout(this._mouseLeaveTimeout);
this._mouseLeaveTimeout = setTimeout(this.hidePersonCard.bind(this), 500);
}
showPersonCard() {
if (!this.personCardShouldRender) {
this.personCardShouldRender = true;
}
this.isPersonCardVisible = true;
}
hidePersonCard() {
this.isPersonCardVisible = false;
const personCard = (this.querySelector('mgt-person-card') ||
this.renderRoot.querySelector('mgt-person-card'));
if (personCard) {
personCard.isExpanded = false;
}
}
getImage() {
if (this.personImage && this.personImage !== '@') {
return this.personImage;
}
else if (this.personDetails && this.personDetails.personImage) {
return this.personDetails.personImage;
}
return null;
}
renderFlyout(anchor) {
if (this.personCardInteraction === PersonCardInteraction.none) {
return anchor;
}
const image = this.getImage();
const flyout = this.personCardShouldRender
? html `
<div slot="flyout" class="flyout">
${this.renderTemplate('person-card', { person: this.personDetails, personImage: image }) ||
html `
<mgt-person-card .personDetails=${this.personDetails} .personImage=${image}> </mgt-person-card>
`}
</div>
`
: null;
return html `
<mgt-flyout .isOpen=${this.isPersonCardVisible}>
${anchor} ${flyout}
</mgt-flyout>
`;
}
renderDetails() {
if (this.showEmail || this.showName) {
const isLarge = this.showEmail && this.showName;
const detailsClasses = {
Details: true,
small: !isLarge
};
return html `
<span class="${classMap(detailsClasses)}">
${this.renderNameAndEmail()}
</span>
`;
}
return null;
}
renderImage(image) {
if (this.personDetails) {
const title = this.personCardInteraction === PersonCardInteraction.none ? this.personDetails.displayName : '';
const isLarge = this.showEmail && this.showName;
const imageClasses = {
initials: !image,
'row-span-2': isLarge,
small: !isLarge,
'user-avatar': true
};
let imageHtml;
if (image) {
imageHtml = html `
<img alt=${title} src=${image} />
`;
}
else {
const initials = this.getInitials();
imageHtml = html `
<span class="initials-text" aria-label="${initials}">
${initials}
</span>
`;
}
return html `
<div class=${classMap(imageClasses)} title=${title} aria-label=${title}>
${imageHtml}
</div>
`;
}
return this.renderEmptyImage();
}
renderEmptyImage() {
const isLarge = this.showEmail && this.showName;
const imageClasses = {
'avatar-icon': true,
'ms-Icon': true,
'ms-Icon--Contact': true,
'row-span-2': isLarge,
small: !isLarge
};
return html `
<i class=${classMap(imageClasses)}></i>
`;
}
renderNameAndEmail() {
if (!this.personDetails || (!this.showEmail && !this.showName)) {
return;
}
const nameView = this.showName
? html `
<div class="user-name" aria-label="${this.personDetails.displayName}">${this.personDetails.displayName}</div>
`
: null;
let emailView;
if (this.showEmail) {
const email = getEmailFromGraphEntity(this.personDetails);
emailView = html `
<div class="user-email" aria-label="${email}">${email}</div>
`;
}
return html `
${nameView} ${emailView}
`;
}
getInitials() {
if (!this.personDetails) {
return '';
}
let initials = '';
if (this.personDetails.givenName) {
initials += this.personDetails.givenName[0].toUpperCase();
}
if (this.personDetails.surname) {
initials += this.personDetails.surname[0].toUpperCase();
}
if (!initials && this.personDetails.displayName) {
const name = this.personDetails.displayName.split(' ');
for (let i = 0; i < 2 && i < name.length; i++) {
if (name[i][0].match(/[a-z]/i)) {
// check if letter
initials += name[i][0].toUpperCase();
}
}
}
return initials;
}
};
__decorate([
property({
attribute: 'person-query'
})
], MgtPerson.prototype, "personQuery", void 0);
__decorate([
property({
attribute: 'user-id'
})
], MgtPerson.prototype, "userId", void 0);
__decorate([
property({
attribute: 'show-name',
type: Boolean
})
], MgtPerson.prototype, "showName", void 0);
__decorate([
property({
attribute: 'show-email',
type: Boolean
})
], MgtPerson.prototype, "showEmail", void 0);
__decorate([
property({
attribute: 'person-details',
type: Object
})
], MgtPerson.prototype, "personDetails", void 0);
__decorate([
property({
attribute: 'person-image',
reflect: true,
type: String
})
], MgtPerson.prototype, "personImage", void 0);
__decorate([
property({
attribute: 'person-card',
converter: (value, type) => {
value = value.toLowerCase();
if (typeof PersonCardInteraction[value] === 'undefined') {
return PersonCardInteraction.none;
}
else {
return PersonCardInteraction[value];
}
}
})
], MgtPerson.prototype, "personCardInteraction", void 0);
__decorate([
property({ attribute: false })
], MgtPerson.prototype, "isPersonCardVisible", void 0);
__decorate([
property({ attribute: false })
], MgtPerson.prototype, "personCardShouldRender", void 0);
MgtPerson = __decorate([
customElement('mgt-person')
], MgtPerson);
export { MgtPerson };
//# sourceMappingURL=mgt-person.js.map