55 * -------------------------------------------------------------------------------------------
66 */
77
8- import { customElement , html , property , query , TemplateResult } from 'lit-element' ;
8+ import { User } from '@microsoft/microsoft-graph-types' ;
9+ import { customElement , html , internalProperty , property , TemplateResult } from 'lit-element' ;
910import { classMap } from 'lit-html/directives/class-map' ;
1011import { repeat } from 'lit-html/directives/repeat' ;
11- import { findPerson , getPeopleFromGroup } from '../../graph/graph.people' ;
12- import { getUser } from '../../graph/graph.user' ;
12+ import { findGroups , GroupType } from '../../graph/graph.groups' ;
13+ import { findPeople , getPeopleFromGroup , PersonType } from '../../graph/graph.people' ;
14+ import { findUsers , getUser } from '../../graph/graph.user' ;
1315import { IDynamicPerson } from '../../graph/types' ;
1416import { Providers } from '../../Providers' ;
1517import { ProviderState } from '../../providers/IProvider' ;
@@ -19,6 +21,8 @@ import { MgtFlyout } from '../sub-components/mgt-flyout/mgt-flyout';
1921import { MgtTemplatedComponent } from '../templatedComponent' ;
2022import { styles } from './mgt-people-picker-css' ;
2123
24+ export { GroupType } from '../../graph/graph.groups' ;
25+ export { PersonType } from '../../graph/graph.people' ;
2226/**
2327 * An interface used to mark an object as 'focused',
2428 * so it can be rendered differently.
@@ -97,6 +101,78 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
97101 this . requestStateUpdate ( true ) ;
98102 }
99103
104+ /**
105+ * value determining if search is filtered to a group.
106+ * @type {string }
107+ */
108+ @property ( {
109+ attribute : 'type' ,
110+ converter : ( value , type ) => {
111+ if ( ! value || value . length === 0 ) {
112+ return PersonType . Any ;
113+ }
114+
115+ if ( typeof PersonType [ value ] === 'undefined' ) {
116+ return PersonType . Any ;
117+ } else {
118+ return PersonType [ value ] ;
119+ }
120+ }
121+ } )
122+ public get type ( ) : PersonType {
123+ return this . _type ;
124+ }
125+ public set type ( value ) {
126+ if ( this . _type === value ) {
127+ return ;
128+ }
129+
130+ this . _type = value ;
131+ this . requestStateUpdate ( true ) ;
132+ }
133+
134+ /**
135+ * type of group to search for - requires personType to be
136+ * set to "Group" or "All"
137+ * @type {string }
138+ */
139+ @property ( {
140+ attribute : 'group-type' ,
141+ converter : ( value , type ) => {
142+ if ( ! value || value . length === 0 ) {
143+ return GroupType . Any ;
144+ }
145+
146+ const values = value . split ( ',' ) ;
147+ const groupTypes = [ ] ;
148+
149+ for ( let v of values ) {
150+ v = v . trim ( ) ;
151+ if ( typeof GroupType [ v ] !== 'undefined' ) {
152+ groupTypes . push ( GroupType [ v ] ) ;
153+ }
154+ }
155+
156+ if ( groupTypes . length === 0 ) {
157+ return GroupType . Any ;
158+ }
159+
160+ // tslint:disable-next-line:no-bitwise
161+ return groupTypes . reduce ( ( a , c ) => a | c ) ;
162+ }
163+ } )
164+ public get groupType ( ) : GroupType {
165+ return this . _groupType ;
166+ }
167+ public set groupType ( value ) {
168+ if ( this . _groupType === value ) {
169+ return ;
170+ }
171+
172+ this . _groupType = value ;
173+ this . requestStateUpdate ( true ) ;
174+ }
175+
100176 /**
101177 * User input in search.
102178 *
@@ -121,12 +197,17 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
121197 @property ( { attribute : false } ) private _showLoading : boolean ;
122198
123199 private _groupId : string ;
200+ private _type : PersonType = PersonType . Person ;
201+ private _groupType : GroupType = GroupType . Any ;
202+
124203 // tracking of user arrow key input for selection
125204 private _arrowSelectionCount : number = 0 ;
126205 // List of people requested if group property is provided
127206 private _groupPeople : IDynamicPerson [ ] ;
128207 private _debouncedSearch : { ( ) : void ; ( ) : void } ;
129208
209+ @internalProperty ( ) private _foundPeople : IDynamicPerson [ ] ;
210+
130211 constructor ( ) {
131212 super ( ) ;
132213
@@ -201,7 +282,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
201282 * @memberof MgtPeoplePicker
202283 */
203284 public render ( ) : TemplateResult {
204- const defaultTemplate = this . renderTemplate ( 'default' , { people : this . people } ) ;
285+ const defaultTemplate = this . renderTemplate ( 'default' , { people : this . _foundPeople } ) ;
205286 if ( defaultTemplate ) {
206287 return defaultTemplate ;
207288 }
@@ -231,7 +312,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
231312 protected requestStateUpdate ( force ?: boolean ) {
232313 if ( force ) {
233314 this . _groupPeople = null ;
234- this . people = null ;
315+ this . _foundPeople = null ;
235316 this . selectedPeople = [ ] ;
236317 }
237318
@@ -329,11 +410,13 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
329410 return this . renderLoading ( ) ;
330411 }
331412
332- if ( ! this . people || this . people . length === 0 || this . showMax === 0 ) {
413+ let people = this . _foundPeople ;
414+
415+ if ( ! people || people . length === 0 || this . showMax === 0 ) {
333416 return this . renderNoData ( ) ;
334417 }
335418
336- const people = this . people . slice ( 0 , this . showMax ) ;
419+ people = people . slice ( 0 , this . showMax ) ;
337420 ( people [ 0 ] as IFocusable ) . isFocused = true ;
338421
339422 return this . renderSearchResults ( people ) ;
@@ -389,7 +472,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
389472 * @memberof MgtPeoplePicker
390473 */
391474 protected renderSearchResults ( people ?: IDynamicPerson [ ] ) {
392- people = people || this . people ;
475+ people = people || this . _foundPeople ;
393476
394477 return html `
395478 < div class ="people-list ">
@@ -421,13 +504,21 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
421504 * @memberof MgtPeoplePicker
422505 */
423506 protected renderPersonResult ( person : IDynamicPerson ) : TemplateResult {
507+ const user = person as User ;
508+ const subTitle = user . jobTitle || user . mail ;
509+
510+ const classes = {
511+ 'people-person-job-title' : true ,
512+ uppercase : ! ! user . jobTitle
513+ } ;
514+
424515 return (
425516 this . renderTemplate ( 'person' , { person } , person . id ) ||
426517 html `
427518 < mgt-person .personDetails =${ person } .personImage =${ '@' } > </ mgt-person >
428519 < div class ="people-person-text-area " id ="${ person . displayName } ">
429520 ${ this . renderHighlightText ( person ) }
430- < span class ="people-person-job-title "> ${ person . jobTitle } </ span >
521+ < span class ="${ classMap ( classes ) } "> ${ subTitle } </ span >
431522 </ div >
432523 `
433524 ) ;
@@ -458,37 +549,73 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
458549 * set's `this.groupPeople` to those members.
459550 */
460551 protected async loadState ( ) : Promise < void > {
552+ let people = this . people ;
553+ const input = this . userInput . toLowerCase ( ) ;
554+
461555 const provider = Providers . globalProvider ;
462- if ( ! provider || provider . state !== ProviderState . SignedIn ) {
463- return ;
464- }
556+ if ( ! people && provider && provider . state === ProviderState . SignedIn ) {
557+ if ( this . groupId ) {
558+ if ( this . _groupPeople === null ) {
559+ try {
560+ const graph = provider . graph . forComponent ( this ) ;
561+ this . _groupPeople = await getPeopleFromGroup ( graph , this . groupId ) ;
562+ } catch ( _ ) {
563+ this . _groupPeople = [ ] ;
564+ }
565+ }
465566
466- const input = this . userInput . toLowerCase ( ) ;
467- let people : IDynamicPerson [ ] ;
567+ people = this . _groupPeople || [ ] ;
568+ } else if ( input ) {
569+ const graph = provider . graph . forComponent ( this ) ;
570+ people = [ ] ;
468571
469- if ( this . groupId ) {
470- if ( this . _groupPeople === null ) {
471- try {
472- const graph = provider . graph . forComponent ( this ) ;
473- this . _groupPeople = await getPeopleFromGroup ( graph , this . groupId ) ;
474- } catch ( _ ) {
475- this . _groupPeople = [ ] ;
572+ if ( this . type === PersonType . Person || this . type === PersonType . Any ) {
573+ try {
574+ people = ( await findPeople ( graph , input , this . showMax ) ) || [ ] ;
575+ } catch ( e ) {
576+ // nop
577+ }
578+
579+ if ( people . length < this . showMax ) {
580+ try {
581+ const users = ( await findUsers ( graph , input , this . showMax ) ) || [ ] ;
582+
583+ // make sure only unique people
584+ const peopleIds = new Set ( people . map ( p => p . id ) ) ;
585+ for ( const user of users ) {
586+ if ( ! peopleIds . has ( user . id ) ) {
587+ people . push ( user ) ;
588+ }
589+ }
590+ } catch ( e ) {
591+ // nop
592+ }
593+ }
476594 }
477- }
478595
479- people = this . _groupPeople || [ ] ;
480- } else if ( input ) {
481- const graph = provider . graph . forComponent ( this ) ;
482- people = await findPerson ( graph , input ) ;
596+ if ( ( this . type === PersonType . Group || this . type === PersonType . Any ) && people . length < this . showMax ) {
597+ try {
598+ const groups = ( await findGroups ( graph , input , this . showMax , this . groupType ) ) || [ ] ;
599+ people = people . concat ( groups ) ;
600+ } catch ( e ) {
601+ // nop
602+ }
603+ }
604+ }
483605 }
484606
485607 if ( people ) {
486- people = people . filter ( ( person : IDynamicPerson ) => {
487- return person . displayName . toLowerCase ( ) . indexOf ( input ) !== - 1 ;
608+ people = people . filter ( ( user : User ) => {
609+ return (
610+ user . displayName . toLowerCase ( ) . indexOf ( input ) !== - 1 ||
611+ ( ! ! user . givenName && user . givenName . toLowerCase ( ) . indexOf ( input ) !== - 1 ) ||
612+ ( ! ! user . surname && user . surname . toLowerCase ( ) . indexOf ( input ) !== - 1 ) ||
613+ ( ! ! user . mail && user . mail . toLowerCase ( ) . indexOf ( input ) !== - 1 )
614+ ) ;
488615 } ) ;
489616 }
490617
491- this . people = this . filterPeople ( people ) ;
618+ this . _foundPeople = this . filterPeople ( people ) ;
492619 }
493620
494621 /**
@@ -545,7 +672,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
545672 this . selectedPeople = [ ...this . selectedPeople , person ] ;
546673 this . fireCustomEvent ( 'selectionChanged' , this . selectedPeople ) ;
547674
548- this . people = [ ] ;
675+ this . _foundPeople = [ ] ;
549676 }
550677 }
551678 }
@@ -602,7 +729,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
602729 if ( event . code === 'Escape' ) {
603730 input . value = '' ;
604731 this . userInput = '' ;
605- this . people = [ ] ;
732+ this . _foundPeople = [ ] ;
606733 return ;
607734 }
608735 if ( event . code === 'Backspace' && this . userInput . length === 0 && this . selectedPeople . length > 0 ) {
@@ -632,7 +759,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
632759 if ( ! this . _debouncedSearch ) {
633760 this . _debouncedSearch = debounce ( async ( ) => {
634761 if ( ! this . userInput . length ) {
635- this . people = [ ] ;
762+ this . _foundPeople = [ ] ;
636763 this . hideFlyout ( ) ;
637764 this . _showLoading = true ;
638765 } else {
@@ -671,10 +798,10 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
671798 }
672799 }
673800 if ( event . code === 'Tab' || event . code === 'Enter' ) {
674- if ( this . people . length ) {
801+ if ( this . _foundPeople . length ) {
675802 event . preventDefault ( ) ;
676803 }
677- this . addPerson ( this . people [ this . _arrowSelectionCount ] ) ;
804+ this . addPerson ( this . _foundPeople [ this . _arrowSelectionCount ] ) ;
678805 this . hideFlyout ( ) ;
679806 ( event . target as HTMLInputElement ) . value = '' ;
680807 }
@@ -685,7 +812,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
685812 * @param event - tracks user key selection
686813 */
687814 private handleArrowSelection ( event : KeyboardEvent ) : void {
688- if ( this . people . length ) {
815+ if ( this . _foundPeople . length ) {
689816 // update arrow count
690817 if ( event . keyCode === 38 ) {
691818 // up arrow
@@ -697,7 +824,10 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
697824 }
698825 if ( event . keyCode === 40 ) {
699826 // down arrow
700- if ( this . _arrowSelectionCount + 1 !== this . people . length && this . _arrowSelectionCount + 1 < this . showMax ) {
827+ if (
828+ this . _arrowSelectionCount + 1 !== this . _foundPeople . length &&
829+ this . _arrowSelectionCount + 1 < this . showMax
830+ ) {
701831 this . _arrowSelectionCount ++ ;
702832 } else {
703833 this . _arrowSelectionCount = 0 ;
0 commit comments