Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
1265 lines (1117 sloc) 44.4 KB
import * as angular from 'angular';
import { PersonaStyleEnum } from '../../core/personaStyleEnum';
import { PersonaSize } from '../persona/sizeEnum';
import { IPerson } from '../../core/person';
import { IconEnum } from '../icon/iconEnum';
let peopleSearchEventName: string = 'uif-people-search';
/**
* @ngdoc interface
* @name IPersonPicker
* @module officeuifabric.components.peoplepicker
*
* @description
* Helper interface used by the people picker directive.
*
* @property {interface} group - Reference to person's group
* @property {Array of IPersonPicker} additionalData - Additional persons to populate under current person
*/
export interface IPersonPicker extends IPerson {
group: IGroup;
additionalData: IPersonPicker[];
}
/**
* @ngdoc interface
* @name IGroup
* @module officeuifabric.components.peoplepicker
*
* @description
* Helper interface used by the people picker directive.
*
* @property {string} name - Reference to person's group
* @property {number} order - Group order, used when displaying search result with groups
*/
export interface IGroup {
name: string;
order: number;
}
/**
* @ngdoc class
* @name GroupedPeopleData
* @module officeuifabric.components.peoplepicker
*
* @description
* Helper class used by the people picker directive.
*
* @property {interface} group - Group reference
* @property {Array of IPersonPicker} people - Persons who belongs to this particular group
*/
export class GroupedPeopleData {
public group: IGroup;
public people: IPersonPicker[] = [];
}
/**
* @ngdoc controller
* @name PeoplePickerController
* @module officeuifabric.components.peoplepicker
*
* @description
* Controller used for the `<uif-people-picker>` directive.
*/
export class PeoplePickerController {
public static $inject: string[] = ['$scope', '$filter', '$element'];
constructor(
private $scope: IPeoplePickerScope,
private $filter: angular.IFilterService,
private $element: angular.IAugmentedJQuery) {
}
public getSelectedPersons(): IPersonPicker[] {
return this.$scope.selectedPersons;
}
public pickerType(): string {
let type: string = this.$scope.type;
if (angular.isUndefined(type)) {
return PeoplePickerTypes[PeoplePickerTypes.grouped];
}
return this.$scope.type;
}
public searchQuery(): string {
return this.$scope.searchQuery;
}
public search(): void {
this.bindPeople(this.$scope.searchQuery);
this.$scope.$broadcast(peopleSearchEventName, this.searchQuery());
}
public bindPeople(query?: string): void {
let peopleData: IPersonPicker[] | angular.IPromise<IPersonPicker[]> = this.$scope.peopleCallback()(query);
peopleData = peopleData || [];
if (peopleData instanceof Array) { // array
this.$scope.groups = this.createPeopleDataStructure(peopleData);
} else if (typeof peopleData.then === 'function') { // promise, async scenario
let searchMoreCtrl: PeopleSearchMoreController = angular.element(this.$element[0].querySelector('.ms-PeoplePicker-searchMore'))
.controller(`${PeopleSearchMoreDirective.directiveName}`);
if (searchMoreCtrl) {
searchMoreCtrl.isSearching(true);
}
let that: PeoplePickerController = this;
peopleData
.then(data => {
that.$scope.groups = this.createPeopleDataStructure(data);
})
.finally(() => {
if (searchMoreCtrl) {
searchMoreCtrl.isSearching(false);
}
});
}
}
private createPeopleDataStructure(people: IPersonPicker[]): GroupedPeopleData[] {
let peopleData: GroupedPeopleData[] = [];
angular.forEach(people, (person: IPersonPicker) => {
let existingGroups: GroupedPeopleData[] = this.$filter('filter')(peopleData, { group: person.group });
let hasGroup: boolean = existingGroups.length === 1;
if (!hasGroup) {
let newPeopleData: GroupedPeopleData = new GroupedPeopleData();
newPeopleData.group = person.group;
newPeopleData.people.push(person);
peopleData.push(newPeopleData);
} else {
let existingData: GroupedPeopleData = existingGroups[0];
existingData.people.push(person);
}
});
return peopleData;
}
}
/**
* @ngdoc enum
* @name PeoplePickerTypes
* @module officeuifabric.components.peoplepicker
*
* @description
* Determines people picker type
*/
enum PeoplePickerTypes {
grouped = 0,
compact = 1,
memberList = 2,
facePile = 3
}
/**
* @ngdoc interface
* @name IPeoplePickerAttributes
* @module officeuifabric.components.peoplepicker
*
* @description
* Interface describing attributes for people picker directive
*
* @property {string} uifType - People picker type
* @property {string} ngDisabled - Disabled state varible name
* @property {string} uifSingle - Allow only one person to be selected
*/
interface IPeoplePickerAttributes extends angular.IAttributes {
uifType: string;
ngDisabled: string;
uifSingle: string;
}
/**
* @ngdoc interface
* @name IPeoplePickerScope
* @module officeuifabric.components.peoplepicker
*
* @description
* Interface describing scope for people picker directive
*
* @property {object} ngModel - Reference to NgModelController
* @property {function} peopleCallback - Function called every time when search event occur
* @property {string} searchQuery - Search query typed into the search input
* @property {string} placeholder - Placeholder for the search input
* @property {string} type - People picker type, based on PeoplePickerTypes enum
* @property {number} delay - Used by type-ahead scenario, dealy after which search query will be run
* @property {object} groups - Groups used by the people picker
* @property {function} onPeoplePickerActive - Reference to NgModelController
* @property {function} addPersonToSelectedPeople - Function called when user click on person in search results
* @property {function} removePersonFromSelectedPeople - Function called when user click on close icon under selected persons
* @property {array} selectedPersons - People picker's selected persons
* @property {function} removePersonFromSearchResults - Function called when user click on close icon under search results
* @property {function} onSearchKeyUp - Callback for the keyup event
* @property {string} facePileHeader - FacePile header, used only in face pile mode
* @property {boolean} ngDisabled - Support for ng-disabled directive
* @property {string} ngChange - Expression to evaluate when selectedPersons changes
*/
export interface IPeoplePickerScope extends angular.IScope {
ngModel: angular.INgModelController;
peopleCallback: () => (query: string) => IPersonPicker[] | angular.IPromise<IPersonPicker[]>;
searchQuery: string;
placeholder: string;
type: string;
delay: number;
groups: GroupedPeopleData[];
onPeoplePickerActive: ($event: KeyboardEvent | MouseEvent) => void;
addPersonToSelectedPeople: (person: IPersonPicker) => void;
removePersonFromSelectedPeople: (person: IPersonPicker, $event: MouseEvent) => void;
selectedPersons: IPersonPicker[];
removePersonFromSearchResults: (people: IPersonPicker[], person: IPersonPicker, $event: MouseEvent) => void;
onSearchKeyUp: ($event: KeyboardEvent) => void;
facePileHeader: string;
ngDisabled: boolean;
ngChange: () => () => void;
}
/**
* @ngdoc directive
* @name uifPeoplePicker
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-people-picker>` directive.
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-people-picker
* uif-people="onSearch(query)"
* ng-model="selectedPeople"
* placeholder="Search for people"
* uif-selected-person-click="personClicked">
* <uif-people-search-more>
* <uif-secondary-text>Showing {{sourcePeople.length}} results</uif-secondary-text>
* <uif-primary-text uif-search-for-text="You are searching for: ">Search organization people</uif-primary-text>
* </uif-people-search-more>
* </uif-people-picker>
*/
export class PeoplePickerDirective implements angular.IDirective {
public static directiveName: string = 'uifPeoplePicker';
public replace: boolean = true;
public require: string[] = ['ngModel', `${PeoplePickerDirective.directiveName}`];
public restrict: string = 'E';
public transclude: boolean = true;
public controller: any = PeoplePickerController;
public scope: {} = {
delay: '@uifSearchDelay',
facePileHeader: '@?uifFacepileHeader',
ngChange: '&?',
ngDisabled: '=?',
ngModel: '=',
onSelectedPersonClick: '&?uifSelectedPersonClick',
peopleCallback: '&uifPeople',
placeholder: '@?',
type: '@?uifType'
};
private templateTypes: { [peoplePickerType: number]: string } = {};
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = (
$document: angular.IDocumentService, $timeout: angular.ITimeoutService, $log: angular.ILogService, $window: angular.IWindowService) =>
new PeoplePickerDirective($document, $timeout, $log, $window);
directive.$inject = ['$document', '$timeout', '$log', '$window'];
return directive;
}
constructor(
private $document: angular.IDocumentService,
private $timeout: angular.ITimeoutService,
private $log: angular.ILogService,
private $window: angular.IWindowService) {
this.templateTypes[PeoplePickerTypes.grouped] =
`<div class="ms-PeoplePicker">
<div class="ms-PeoplePicker-searchBox">
<div class="ms-PeoplePicker-persona" ng-repeat="person in selectedPersons track by $index">
<uif-persona ng-click="onSelectedPersonClick()(person)"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.square]}"
uif-size="${PersonaSize[PersonaSize.xsmall]}"
uif-presence="{{person.presence}}"
uif-image-url="{{person.icon}}">
<uif-persona-initials uif-color="{{person.color}}">{{person.initials}}</uif-persona-initials>
<uif-persona-primary-text>{{person.primaryText}}</uif-persona-primary-text>
</uif-persona>
<button
ng-if="!ngDisabled"
type="button"
ng-click="removePersonFromSelectedPeople(person, $event)"
class="ms-PeoplePicker-personaRemove">
<uif-icon uif-type="${IconEnum[IconEnum.x]}"></uif-icon>
</button>
</div>
<input ng-click="onPeoplePickerActive($event)"
placeholder="{{placeholder}}"
ng-model="searchQuery"
class="ms-PeoplePicker-searchField"
ng-focus="onPeoplePickerActive($event)"
ng-keyup="onSearchKeyUp($event)"
type="text">
</div>
<div class="ms-PeoplePicker-results">
<div class="ms-PeoplePicker-resultGroups">
<div class="ms-PeoplePicker-resultGroup" ng-repeat="groupData in groups | orderBy:'-order'">
<div class="ms-PeoplePicker-resultGroupTitle">{{groupData.group.name}}</div>
<uif-people-picker-result-list
ng-model="groupData.people"
uif-person-click="addPersonToSelectedPeople"
uif-person-close-click="removePersonFromSearchResults"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.square]}"
uif-size="${PersonaSize[PersonaSize.medium]}"></uif-people-picker-result-list>
</div>
</div>
<ng-transclude />
</div>
</div>`;
this.templateTypes[PeoplePickerTypes.compact] =
`<div class="ms-PeoplePicker ms-PeoplePicker--compact">
<div class="ms-PeoplePicker-searchBox">
<div class="ms-PeoplePicker-persona" ng-repeat="person in selectedPersons track by $index">
<uif-persona ng-click="onSelectedPersonClick()(person)"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.square]}"
uif-size="${PersonaSize[PersonaSize.xsmall]}"
uif-presence="{{person.presence}}"
uif-image-url="{{person.icon}}">
<uif-persona-initials uif-color="{{person.color}}">{{person.initials}}</uif-persona-initials>
<uif-persona-primary-text>{{person.primaryText}}</uif-persona-primary-text>
</uif-persona>
<button
ng-if="!ngDisabled"
type="button"
ng-click="removePersonFromSelectedPeople(person, $event)"
class="ms-PeoplePicker-personaRemove">
<uif-icon uif-type="${IconEnum[IconEnum.x]}"></uif-icon>
</button>
</div>
<input ng-click="onPeoplePickerActive($event)"
ng-model="searchQuery"
placeholder="{{placeholder}}"
class="ms-PeoplePicker-searchField"
ng-focus="onPeoplePickerActive($event)"
ng-keyup="onSearchKeyUp($event)"
type="text">
</div>
<div class="ms-PeoplePicker-results">
<div class="ms-PeoplePicker-resultGroups">
<div class="ms-PeoplePicker-resultGroup" ng-repeat="groupData in groups | orderBy:'-order'">
<div class="ms-PeoplePicker-resultGroupTitle">{{groupData.group.name}}</div>
<uif-people-picker-result-list
ng-model="groupData.people"
uif-picker-type="${PeoplePickerTypes[PeoplePickerTypes.compact]}"
uif-person-click="addPersonToSelectedPeople"
uif-person-close-click="removePersonFromSearchResults"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.square]}"
uif-size="${PersonaSize[PersonaSize.xsmall]}"></uif-people-picker-result-list>
</div>
</div>
<ng-transclude />
</div>
</div>`;
this.templateTypes[PeoplePickerTypes.memberList] = `
<div class="ms-PeoplePicker ms-PeoplePicker--membersList">
<div class="ms-PeoplePicker-searchBox">
<input ng-click="onPeoplePickerActive($event)"
placeholder="{{placeholder}}"
ng-model="searchQuery"
class="ms-PeoplePicker-searchField"
ng-focus="onPeoplePickerActive($event)"
ng-keyup="onSearchKeyUp($event)"
type="text">
</div>
<div class="ms-PeoplePicker-results">
<div class="ms-PeoplePicker-resultGroups">
<div class="ms-PeoplePicker-resultGroup" ng-repeat="groupData in groups | orderBy:'-order'">
<uif-people-picker-result-list
ng-model="groupData.people"
uif-person-click="addPersonToSelectedPeople"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.round]}"
uif-size="${PersonaSize[PersonaSize.medium]}"></uif-people-picker-result-list>
</div>
</div>
</div>
<uif-people-picker-selected ng-model="selectedPersons"
uif-selected-person-click="onSelectedPersonClick()"
uif-person-close="removePersonFromSelectedPeople">
<ng-transclude></ng-transclude>
</uif-people-picker-selected>
</div>`;
this.templateTypes[PeoplePickerTypes.facePile] = `
<div class="ms-PeoplePicker ms-PeoplePicker--Facepile">
<div class="ms-PeoplePicker-searchBox">
<input ng-click="onPeoplePickerActive($event)"
placeholder="{{placeholder}}"
ng-model="searchQuery"
class="ms-PeoplePicker-searchField"
ng-focus="onPeoplePickerActive($event)"
ng-keyup="onSearchKeyUp($event)"
type="text">
</div>
<div class="ms-PeoplePicker-results">
<div class="ms-PeoplePicker-peopleListHeader">
<span>{{facePileHeader}}</span>
</div>
<div ng-repeat="groupData in groups | orderBy:'-order'">
<uif-people-picker-result-list
ng-model="groupData.people"
uif-person-click="addPersonToSelectedPeople"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.round]}"
uif-size="${PersonaSize[PersonaSize.small]}"></uif-people-picker-result-list>
</div>
<div class="uif-search-more"></div>
</div>
<uif-people-picker-selected ng-model="selectedPersons"
uif-selected-person-click="onSelectedPersonClick()"
uif-person-close="removePersonFromSelectedPeople">
<div class="uif-people-header"></div>
</uif-people-picker-selected>
</div>`;
}
public template: any = ($element: angular.IAugmentedJQuery, $attrs: IPeoplePickerAttributes) => {
let type: string = $attrs.uifType;
if (angular.isUndefined(type)) {
return this.templateTypes[PeoplePickerTypes.grouped];
}
if (PeoplePickerTypes[type] === undefined) {
this.$log.error('Error [ngOfficeUiFabric] officeuifabric.components.peoplepicker - unsupported people picker type:\n' +
'the type \'' + type + '\' is not supported by ng-Office UI Fabric as valid type for people picker.' +
'Supported types can be found under PeoplePickerTypes enum here:\n' +
'https://github.com/ngOfficeUIFabric/ng-officeuifabric/blob/master/src/components/peoplepicker/peoplePickerDirective.ts');
throw '[ngOfficeUiFabric] - Error';
}
return this.templateTypes[PeoplePickerTypes[type]];
}
public link: angular.IDirectiveLinkFn = (
$scope: IPeoplePickerScope,
$element: angular.IAugmentedJQuery,
$attrs: IPeoplePickerAttributes,
ctrls: [angular.INgModelController, PeoplePickerController],
$transclude: angular.ITranscludeFunction): void => {
let ngModelCtrl: angular.INgModelController = ctrls[0];
let peoplePickerCtrl: PeoplePickerController = ctrls[1];
this.initDisabledState($element, $scope, $attrs);
$scope.facePileHeader = $scope.facePileHeader || 'Suggested contacts';
$scope.$watchCollection('selectedPersons', (data: any, data2: any, data3: any) => {
this.resizeSearchField($element);
if ($scope.ngChange) {
$scope.ngChange();
}
});
ngModelCtrl.$render = () => {
if (ngModelCtrl.$viewValue) {
$scope.selectedPersons = ngModelCtrl.$viewValue;
} else {
$scope.selectedPersons = [];
}
this.resizeSearchField($element);
};
peoplePickerCtrl.search();
let searchTimeout: angular.IPromise<void> = null;
$scope.onSearchKeyUp = ($event: KeyboardEvent) => {
let $searchMore: JQuery = angular.element($element[0].querySelector('.ms-PeoplePicker-searchMore'));
if ($searchMore.length !== 0) {
$scope.searchQuery ? $element.addClass('is-searching') : $element.removeClass('is-searching');
$scope.searchQuery ? $searchMore.addClass('is-active') : $searchMore.removeClass('is-active');
this.animateSelectedPeople($element);
}
if (!$scope.delay) {
return;
}
if (searchTimeout != null) {
this.$timeout.cancel(searchTimeout);
}
searchTimeout = this.$timeout(
() => {
peoplePickerCtrl.search();
},
$scope.delay);
};
$scope.onPeoplePickerActive = ($event: KeyboardEvent | MouseEvent) => {
this.smoothScrollTo($element[0]);
if ($scope.type !== PeoplePickerTypes[PeoplePickerTypes.facePile]) {
let $results: JQuery = angular.element($element[0].querySelector('.ms-PeoplePicker-results'));
$results[0].style.width = $element[0].clientWidth - 2 + 'px';
} else if ($scope.type === PeoplePickerTypes[PeoplePickerTypes.facePile]) {
this.animateSelectedPeople($element);
}
$event.stopPropagation();
$element.addClass('is-active');
};
$scope.addPersonToSelectedPeople = (person) => {
if ($scope.selectedPersons.indexOf(person) !== -1) {
return;
}
if (angular.isDefined($attrs.uifSingle) && $scope.selectedPersons.length > 0) {
$scope.selectedPersons.length = 0;
}
$scope.selectedPersons.push(person);
ngModelCtrl.$setViewValue($scope.selectedPersons);
};
$scope.removePersonFromSelectedPeople = (person: IPersonPicker, $event: MouseEvent) => {
let indx: number = $scope.selectedPersons.indexOf(person);
$scope.selectedPersons.splice(indx, 1);
ngModelCtrl.$setViewValue($scope.selectedPersons);
$event.stopPropagation();
};
$scope.removePersonFromSearchResults = (people: IPersonPicker[], person: IPersonPicker, $event: MouseEvent) => {
$event.stopPropagation();
let indx: number = people.indexOf(person);
people.splice(indx, 1);
};
this.$document.on('click', () => {
$element.removeClass('is-active');
});
if ($scope.type === PeoplePickerTypes[PeoplePickerTypes.facePile]) {
$transclude((clone: angular.IAugmentedJQuery) => {
this.insertFacePileHeader(clone, $scope, $element);
this.insertFacePileSearchMore(clone, $scope, $element);
});
}
}
private initDisabledState($element: JQuery, $scope: IPeoplePickerScope, $attrs: IPeoplePickerAttributes): void {
let $searchField: JQuery = angular.element($element[0].querySelector('.ms-PeoplePicker-searchField'));
$attrs.$observe('disabled', (disabled) => {
if (disabled) {
$searchField.attr('disabled', 'disabled');
} else {
$searchField.removeAttr('disabled');
}
});
}
private animateSelectedPeople($element: JQuery): void {
let $selectedPeople: JQuery = angular.element($element[0].querySelector('.ms-PeoplePicker-selectedPeople'));
$selectedPeople.addClass('ms-u-slideDownIn20');
setTimeout(() => { $selectedPeople.removeClass('ms-u-slideDownIn20'); }, 1000);
}
private currentYPosition(): number {
if (this.$window.pageYOffset) {
return this.$window.pageYOffset;
}
let body: HTMLElement = angular.element(this.$document[0]).find('body')[0];
if (body.scrollTop) {
return body.scrollTop;
}
return 0;
}
private elmYPosition(element: HTMLElement): number {
let y: number = element.offsetTop;
let node: any = element;
while (node.offsetParent && node.offsetParent !== document.body) {
node = <Element>(node.offsetParent);
y += node.offsetTop;
}
return y;
}
private smoothScrollTo(element: HTMLElement): void {
let startY: number = this.currentYPosition();
let stopY: number = this.elmYPosition(element);
let distance: number = stopY > startY ? stopY - startY : startY - stopY;
if (distance < 100) {
window.scrollTo(0, stopY);
return;
}
let speed: number = Math.round(distance / 30);
if (speed >= 20) {
speed = 20;
}
let step: number = Math.round(distance / 25);
let leapY: number = stopY > startY ? startY + step : startY - step;
let timer: number = 0;
if (stopY > startY) {
for (let i: number = startY; i < stopY; i += step) {
((lY: number, t: number) => {
setTimeout(
() => {
window.scrollTo(0, lY);
},
t * speed);
})(leapY, timer);
leapY += step;
if (leapY > stopY) {
leapY = stopY;
}
timer++;
}
return;
}
for (let i: number = startY; i > stopY; i -= step) {
((lY: number, t: number) => {
setTimeout(
() => {
window.scrollTo(0, lY);
},
t * speed);
})(leapY, timer);
leapY -= step;
if (leapY < stopY) {
leapY = stopY;
}
timer++;
}
}
private insertFacePileHeader(clone: angular.IAugmentedJQuery, $scope: IPeoplePickerScope, $element: angular.IAugmentedJQuery): void {
let elementToReplace: JQuery = angular.element($element[0].querySelector('.uif-people-header'));
for (let i: number = 0; i < clone.length; i++) {
let element: angular.IAugmentedJQuery = angular.element(clone[i]);
if (element.hasClass('ms-PeoplePicker-selectedCount')) {
elementToReplace.replaceWith(element);
break;
}
}
}
private insertFacePileSearchMore(clone: angular.IAugmentedJQuery, $scope: IPeoplePickerScope, $element: angular.IAugmentedJQuery): void {
let elementToReplace: JQuery = angular.element($element[0].querySelector('.uif-search-more'));
for (let i: number = 0; i < clone.length; i++) {
let element: angular.IAugmentedJQuery = angular.element(clone[i]);
if (element.hasClass('ms-PeoplePicker-searchMore')) {
elementToReplace.replaceWith(element);
break;
}
}
}
private resizeSearchField($peoplePicker: JQuery): void {
let $searchBox: JQuery = angular.element($peoplePicker[0].querySelector('.ms-PeoplePicker-searchBox'));
let $searchField: JQuery = angular.element($peoplePicker[0].querySelector('.ms-PeoplePicker-searchField'));
let searchBoxLeftEdge: number = $searchBox.prop('offsetLeft');
let searchBoxWidth: number = $searchBox[0].clientWidth;
let searchBoxRightEdge: number = searchBoxLeftEdge + searchBoxWidth;
let $personaNodes: NodeListOf<Element> = $searchBox[0].querySelectorAll('.ms-PeoplePicker-persona');
if ($personaNodes.length === 0) {
$searchField[0].style.width = '100%';
return;
}
let $lastPersona: JQuery = angular.element($personaNodes[$personaNodes.length - 1]);
let lastPersonaLeftEdge: number = $lastPersona.prop('offsetLeft');
let lastPersonaWidth: number = $lastPersona[0].clientWidth;
let lastPersonaRightEdge: number = lastPersonaLeftEdge + lastPersonaWidth;
let newFieldWidth: number | string = searchBoxRightEdge - lastPersonaRightEdge - 5;
if (newFieldWidth < 100) {
newFieldWidth = '100%';
$searchField[0].style.width = '100%';
} else {
$searchField[0].style.width = newFieldWidth + 'px';
}
}
}
/**
* @ngdoc interface
* @name IPeoplePickerResultListScope
* @module officeuifabric.components.peoplepicker
*
* @description
* Interface used by the people picker result list directive.
*
* @property {array} people - Persons in search results
* @property {function} expandAdditionalData - Callback when clicking on "expand" button under search results
*/
export interface IPeoplePickerResultListScope extends angular.IScope {
people: IPersonPicker[];
expandAdditionalData: ($event: MouseEvent) => void;
}
/**
* @ngdoc directive
* @name uifPeoplePickerResultList
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-people-picker-result-list>` is a helper directive used by people picker.
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-people-picker-result-list
* ng-model="groupData.people"
* uif-person-click="addPersonToSelectedPeople"
* uif-person-close-click="removePersonFromSearchResults"
* uif-style="${PersonaStyleEnum[PersonaStyleEnum.square]}"
* uif-size="${PersonaSize[PersonaSize.medium]}">
* </uif-people-picker-result-list>
*/
export class PeoplePickerResultListDirective implements angular.IDirective {
public static directiveName: string = 'uifPeoplePickerResultList';
public replace: boolean = true;
public restrict: string = 'E';
public transclude: boolean = true;
public template: string = `
<ul class="ms-PeoplePicker-resultList">
<li class="ms-PeoplePicker-result" ng-repeat="person in people track by $index">
<div role="button" class="ms-PeoplePicker-resultBtn"
ng-class="{'ms-PeoplePicker-resultBtn--compact': pickerType === 'compact'}" ng-click="onPersonClick()(person)">
<uif-persona
uif-style="{{personStyle}}"
uif-size="{{personSize}}"
uif-presence="{{person.presence}}"
uif-image-url="{{person.icon}}">
<uif-persona-initials uif-color="{{person.color}}">{{person.initials}}</uif-persona-initials>
<uif-persona-primary-text>{{person.primaryText}}</uif-persona-primary-text>
<uif-persona-secondary-text>{{person.secondaryText}}</uif-persona-secondary-text>
</uif-persona>
<button type="button"
ng-if="!person.additionalData && onPersonCloseClick()"
ng-click="onPersonCloseClick()(people, person, $event)"
class="ms-PeoplePicker-resultAction js-resultRemove">
<uif-icon uif-type="${IconEnum[IconEnum.x]}"></uif-icon>
</button>
<button type="button"
ng-if="person.additionalData"
ng-click="expandAdditionalData($event)"
class="ms-PeoplePicker-resultAction js-resultRemove">
<uif-icon uif-type="${IconEnum[IconEnum.chevronsDown]}"></uif-icon>
</button>
</div>
<div ng-if="person.additionalData" class="ms-PeoplePicker-resultAdditionalContent">
<uif-people-picker-result-list
ng-model="person.additionalData"
uif-person-click="onPersonClick()"
uif-person-close-click="onPersonCloseClick()"
uif-picker-type="{{pickerType}}"
uif-style="{{personStyle}}"
uif-size="{{personSize}}"></uif-people-picker-result-list>
</div>
</li>
</ul>`;
public scope: {} = {
onPersonClick: '&uifPersonClick',
onPersonCloseClick: '&uifPersonCloseClick',
people: '=ngModel',
personSize: '@uifSize',
personStyle: '@uifStyle',
pickerType: '@uifPickerType'
};
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new PeoplePickerResultListDirective();
return directive;
}
constructor() {
//
}
public link: angular.IDirectiveLinkFn = (
$scope: IPeoplePickerResultListScope,
$element: angular.IAugmentedJQuery,
$attrs: angular.IAttributes,
peoplePickerCtrl: PeoplePickerController,
$transclude: angular.ITranscludeFunction): void => {
$scope.expandAdditionalData = ($event: MouseEvent) => {
$event.stopPropagation();
let $button: JQuery = angular.element($event.target);
for (let i: number = 0; i < 10; i++) {
let $parent: JQuery = $button.parent();
if ($parent.hasClass('ms-PeoplePicker-result')) {
$parent.toggleClass('is-expanded');
break;
}
$button = $parent;
}
};
}
}
/**
* @ngdoc interface
* @name IPeopleSearchMoreScope
* @module officeuifabric.components.peoplepicker
*
* @description
* Interface used by the scope for people search more directive.
*
* @property {function} onSearch - Function called when user click by "search more" component
* @property {boolean} processing - Boolean indicating that search in progress (in async scenarious)
* @property {string} pickerType - Determines people picker type (from parent directive people picker)
* @property {boolean} disconnected - Boolean indicating disconnected scenario
*/
export interface IPeopleSearchMoreScope extends angular.IScope {
onSearch: ($event: MouseEvent) => void;
processing: boolean;
pickerType: string;
disconnected: boolean;
}
/**
* @ngdoc controller
* @name PeopleSearchMoreController
* @module officeuifabric.components.peoplepicker
*
* @description
* Controller used for the `<uif-people-search-more>` directive.
*/
export class PeopleSearchMoreController {
public static $inject: string[] = ['$scope', '$element'];
public searchCallbacks: { (): void; }[] = [];
constructor(
private $scope: IPeopleSearchMoreScope,
private $element: angular.IAugmentedJQuery) {
}
public isSearching(searching: boolean): void {
this.$scope.processing = searching;
searching ? this.$element.addClass('is-searching') : this.$element.removeClass('is-searching');
}
}
export class PeopleSearchMoreDirective implements angular.IDirective {
public static directiveName: string = 'uifPeopleSearchMore';
public replace: boolean = true;
public restrict: string = 'E';
public transclude: boolean = true;
public require: string = `^^${PeoplePickerDirective.directiveName}`;
public controller: any = PeopleSearchMoreController;
public template: string = `
<div class="ms-PeoplePicker-searchMore js-searchMore"
ng-class="{'ms-PeoplePicker-searchMore--disconnected': disconnected}">
<button type="button" ng-if="pickerType === '${PeoplePickerTypes[PeoplePickerTypes.grouped]}' && !disconnected"
ng-click="onSearch($event)" class="ms-PeoplePicker-searchMoreBtn">
<div class="ms-PeoplePicker-searchMoreIcon">
<uif-icon ng-if="!disconnected" uif-type="${IconEnum[IconEnum.search]}"></uif-icon>
<uif-icon ng-if="disconnected" uif-type="${IconEnum[IconEnum.alert]}"></uif-icon>
</div>
<ng-transclude />
</button>
<div role="button" ng-if="pickerType === '${PeoplePickerTypes[PeoplePickerTypes.compact]}' && !disconnected"
ng-click="onSearch($event)" class="ms-PeoplePicker-searchMoreBtn ms-PeoplePicker-searchMoreBtn--compact">
<div class="ms-PeoplePicker-searchMoreIcon">
<uif-icon ng-if="!disconnected" uif-type="${IconEnum[IconEnum.search]}"></uif-icon>
<uif-icon ng-if="disconnected" uif-type="${IconEnum[IconEnum.alert]}"></uif-icon>
</div>
<ng-transclude />
</div>
<div role="button" ng-if="pickerType === '${PeoplePickerTypes[PeoplePickerTypes.facePile]}' && !disconnected"
ng-click="onSearch($event)" class="ms-PeoplePicker-searchMoreBtn ms-PeoplePicker-searchMoreBtn--compact">
<div class="ms-PeoplePicker-searchMoreIcon">
<uif-icon ng-if="!disconnected" uif-type="${IconEnum[IconEnum.search]}"></uif-icon>
<uif-icon ng-if="disconnected" uif-type="${IconEnum[IconEnum.alert]}"></uif-icon>
</div>
<ng-transclude />
</div>
<div role="button" ng-if="disconnected" class="ms-PeoplePicker-searchMoreBtn">
<div class="ms-PeoplePicker-searchMoreIcon">
<uif-icon uif-type="${IconEnum[IconEnum.alert]}"></uif-icon>
</div>
<ng-transclude />
</div>
<uif-spinner ng-show="processing"></uif-spinner>
</div>`;
public scope: {} = {
disconnected: '=uifDisconnected'
};
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new PeopleSearchMoreDirective();
return directive;
}
constructor() {
//
}
public link: angular.IDirectiveLinkFn = (
$scope: IPeopleSearchMoreScope,
$element: angular.IAugmentedJQuery,
$attrs: angular.IAttributes,
peoplePickerCtrl: PeoplePickerController,
$transclude: angular.ITranscludeFunction): void => {
$scope.pickerType = peoplePickerCtrl.pickerType();
$scope.onSearch = ($event: MouseEvent) => {
$event.stopPropagation();
peoplePickerCtrl.search();
$scope.$broadcast(peopleSearchEventName, peoplePickerCtrl.searchQuery());
};
}
}
/**
* @ngdoc interface
* @name IPrimaryTextScope
* @module officeuifabric.components.peoplepicker
*
* @description
* Interface used by the primary text directive.
*
* @property {string} searchingForText - Template for label "Search for"
* @property {string} searchQuery - Search query from parent directive, i.e. people picker
*/
export interface IPrimaryTextScope extends angular.IScope {
searchingForText: string;
searchQuery: string;
}
/**
* @ngdoc controller
* @name PrimaryTextController
* @module officeuifabric.components.peoplepicker
*
* @description
* Controller used for the `<uif-primary-text>` directive.
*/
export class PrimaryTextController {
public static $inject: string[] = ['$scope'];
constructor(
private $scope: IPrimaryTextScope) {
this.$scope.$on(peopleSearchEventName, ($event: angular.IAngularEvent, query: string) => {
this.$scope.searchQuery = query;
});
}
}
/**
* @ngdoc directive
* @name uifPrimaryText
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-primary-text>` is a helper directive for search more component
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-primary-text uif-search-for-text="You are searching for: ">Search organization people</uif-primary-text>
*/
export class PrimaryTextDirective implements angular.IDirective {
public static directiveName: string = 'uifPrimaryText';
public replace: boolean = true;
public restrict: string = 'E';
public require: string[] = [`^^${PeopleSearchMoreDirective.directiveName}`, `^^${PeoplePickerDirective.directiveName}`];
public transclude: boolean = true;
public controller: any = PrimaryTextController;
public template: string = `
<div ng-show="!$parent.$parent.disconnected" class="ms-PeoplePicker-searchMorePrimary">
<div ng-show="$parent.$parent.processing">{{searchingForText}} {{searchQuery}}</div>
<ng-transclude ng-show="!$parent.$parent.processing"></ng-transclude>
</div>`;
public scope: {} = {
searchingForText: '@?uifSearchForText'
};
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new PrimaryTextDirective();
return directive;
}
constructor() {
//
}
public link: angular.IDirectiveLinkFn = (
$scope: IPrimaryTextScope,
$element: angular.IAugmentedJQuery,
$attrs: angular.IAttributes,
ctrls: [PeopleSearchMoreController, PeoplePickerController],
$transclude: angular.ITranscludeFunction): void => {
$scope.searchingForText = $scope.searchingForText || 'Searching for';
}
}
/**
* @ngdoc directive
* @name uifSecondaryText
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-secondary-text>` is a helper directive for search more component
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-secondary-text>Showing {{sourcePeople.length}} results</uif-secondary-text>
*/
export class SecondaryTextDirective implements angular.IDirective {
public static directiveName: string = 'uifSecondaryText';
public replace: boolean = true;
public restrict: string = 'E';
public transclude: boolean = true;
public template: string = `
<div ng-show="!$parent.$parent.disconnected" class="ms-PeoplePicker-searchMoreSecondary">
<ng-transclude></ng-transclude>
</div>`;
public scope: boolean = true;
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new SecondaryTextDirective();
return directive;
}
constructor() {
//
}
}
/**
* @ngdoc directive
* @name uifDisconnectedText
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-disconnected-text>` is a helper directive for search more component
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-disconnected-text> We are having trouble connecting to the server.
* <br> Please try again in a few minutes.
* </uif-disconnected-text>
*/
export class DisconnectedTextDirective implements angular.IDirective {
public static directiveName: string = 'uifDisconnectedText';
public replace: boolean = true;
public restrict: string = 'E';
public transclude: boolean = true;
public template: string = `
<div ng-show="$parent.$parent.disconnected" class="ms-PeoplePicker-searchMorePrimary">
<ng-transclude></ng-transclude>
</div>`;
public scope: boolean = true;
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new DisconnectedTextDirective();
return directive;
}
constructor() {
//
}
}
/**
* @ngdoc directive
* @name uifPeoplePickerSelected
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-people-picker-selected>` is a helper directive used in memeberList and facePile modes
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-people-picker-selected ng-model="selectedPersons"
* uif-selected-person-click="onSelectedPersonClick()"
* uif-person-close="removePersonFromSelectedPeople">
* <ng-transclude></ng-transclude>
* </uif-people-picker-selected>
*/
export class PeoplePickerSelectedDirective implements angular.IDirective {
public static directiveName: string = 'uifPeoplePickerSelected';
public replace: boolean = true;
public restrict: string = 'E';
public transclude: boolean = true;
public template: string = `
<div class="ms-PeoplePicker-selected" ng-class="{'is-active': selectedPeople && selectedPeople.length > 0}">
<div class="ms-PeoplePicker-selectedHeader">
<ng-transclude></ng-transclude>
</div>
<ul class="ms-PeoplePicker-selectedPeople">
<li class="ms-PeoplePicker-selectedPerson" ng-repeat="person in selectedPeople track by $index">
<uif-persona ng-click="onSelectedPersonClick()(person)"
uif-style="${PersonaStyleEnum[PersonaStyleEnum.round]}"
uif-size="${PersonaSize[PersonaSize.small]}"
uif-presence="{{person.presence}}"
uif-image-url="{{person.icon}}">
<uif-persona-initials uif-color="{{person.color}}">{{person.initials}}</uif-persona-initials>
<uif-persona-primary-text>{{person.primaryText}}</uif-persona-primary-text>
<uif-persona-secondary-text>{{person.secondaryText}}</uif-persona-secondary-text>
</uif-persona>
<button type="button"
ng-click="removePersonFromSelectedPeople()(person, $event)"
class="ms-PeoplePicker-resultAction js-resultRemove">
<uif-icon uif-type="${IconEnum[IconEnum.x]}"></uif-icon>
</button>
</li>
</ul>
</div>`;
public scope: {} = {
onSelectedPersonClick: '&?uifSelectedPersonClick',
removePersonFromSelectedPeople: '&uifPersonClose',
selectedPeople: '=ngModel'
};
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new PeoplePickerSelectedDirective();
return directive;
}
constructor() {
//
}
}
/**
* @ngdoc interface
* @name IGroup
* @module officeuifabric.components.peoplepicker
*
* @description
* Helper interface used by the people picker directive.
*
* @property {Array} selectedPersons - Persons selected in people picker
*/
export interface ISelectedPeopleHeaderScope extends angular.IScope {
selectedPersons: IPersonPicker[];
}
/**
* @ngdoc directive
* @name uifSelectedPeopleHeader
* @module officeuifabric.components.peoplepicker
*
* @restrict E
*
* @description
* `<uif-selected-people-header>` is a helper directive used in memeberList and facePile modes
*
* @see {link http://dev.office.com/fabric/components/peoplepicker}
*
* @usage
*
* <uif-selected-people-header>{{selectedPeople.length}} selected person(s)</uif-selected-people-header>
*/
export class SelectedPeopleHeaderDirective implements angular.IDirective {
public static directiveName: string = 'uifSelectedPeopleHeader';
public require: string = `^^${PeoplePickerDirective.directiveName}`;
public replace: boolean = true;
public restrict: string = 'E';
public transclude: boolean = true;
public scope: boolean = true;
public template: string = `<span class="ms-PeoplePicker-selectedCount" ng-transclude></span>`;
public static factory(): angular.IDirectiveFactory {
const directive: angular.IDirectiveFactory = () => new SelectedPeopleHeaderDirective();
return directive;
}
public link: angular.IDirectiveLinkFn = (
$scope: ISelectedPeopleHeaderScope,
$element: angular.IAugmentedJQuery,
$attrs: angular.IAttributes,
peoplePickerCtrl: PeoplePickerController,
$transclude: angular.ITranscludeFunction): void => {
$scope.selectedPersons = peoplePickerCtrl.getSelectedPersons();
}
}
export let module: angular.IModule = angular.module('officeuifabric.components.peoplepicker', [
'officeuifabric.components'])
.directive(PeoplePickerDirective.directiveName, PeoplePickerDirective.factory())
.directive(PrimaryTextDirective.directiveName, PrimaryTextDirective.factory())
.directive(SecondaryTextDirective.directiveName, SecondaryTextDirective.factory())
.directive(PeoplePickerResultListDirective.directiveName, PeoplePickerResultListDirective.factory())
.directive(DisconnectedTextDirective.directiveName, DisconnectedTextDirective.factory())
.directive(PeoplePickerSelectedDirective.directiveName, PeoplePickerSelectedDirective.factory())
.directive(SelectedPeopleHeaderDirective.directiveName, SelectedPeopleHeaderDirective.factory())
.directive(PeopleSearchMoreDirective.directiveName, PeopleSearchMoreDirective.factory());
You can’t perform that action at this time.