import * as React from 'react' ; import { BasePicker , IBasePickerProps , IInputProps , IPickerItemProps } from 'office-ui-fabric-react/lib/Pickers' ; import { IPickerTerm , IPickerTerms } from './ITermPicker' ; import SPTermStorePickerService from './../../services/SPTermStorePickerService' ; import styles from './TaxonomyPicker.module.scss' ; import { ITaxonomyPickerProps } from './ITaxonomyPicker' ; import { BaseComponentContext } from '@microsoft/sp-component-base' ; import * as strings from 'ControlStrings' ; import { Icon } from 'office-ui-fabric-react/lib/Icon' ; import { ITermSet } from "../../services/ISPTermStorePickerService" ; import { Autofill } from 'office-ui-fabric-react/lib/components/Autofill/Autofill' ; import { LegacyRef , KeyboardEvent } from 'react' ; export class TermBasePicker extends BasePicker < IPickerTerm , IBasePickerProps < IPickerTerm > > { } export interface ITermPickerState { terms : IPickerTerms ; elRef ? : LegacyRef < TermBasePicker > & LegacyRef < any > ; } export interface ITermPickerProps { termPickerHostProps : ITaxonomyPickerProps ; context : BaseComponentContext ; disabled : boolean ; value : IPickerTerms ; allowMultipleSelections : boolean ; isTermSetSelectable ? : boolean ; disabledTermIds ? : string [ ] ; disableChildrenOfDisabledParents ? : boolean ; placeholder ? : string ; /** Called when text is in the input field and the enter key is pressed.  */ onNewTerm ? : ( newLabel : string ) => void ; onChanged : ( items : IPickerTerm [ ] ) => void ; onInputChange : ( input : string ) => string ; onBlur : ( ev : React . FocusEvent < HTMLElement | Autofill > ) => void ; } export default class TermPicker extends React . Component < ITermPickerProps , ITermPickerState > { private allTerms : ITermSet = null ; private termsService : SPTermStorePickerService ; /**
     * Constructor method
     */ constructor ( props ) { super ( props ) ; this . onRenderItem = this . onRenderItem . bind ( this ) ; this . onRenderSuggestionsItem = this . onRenderSuggestionsItem . bind ( this ) ; this . onFilterChanged = this . onFilterChanged . bind ( this ) ; this . onGetTextFromItem = this . onGetTextFromItem . bind ( this ) ; this . state = { terms : this . props . value } ; this . termsService = new SPTermStorePickerService ( this . props . termPickerHostProps , this . props . context ) ; } /**
     * componentWillReceiveProps method
     */ public componentWillReceiveProps ( nextProps ) { let newKeys = nextProps . value . map ( a => a . key ) ; let currentKeys = this . state . terms . map ( a => a . key ) ; if ( newKeys . sort ( ) . join ( ',' ) !== currentKeys . sort ( ) . join ( ',' ) ) { this . setState ( { terms : nextProps . value } ) ; } } /**
     * Renders the item in the picker
     */ protected onRenderItem ( term ) { return ( < div className = { styles . pickedTermRoot } key = { term . index } data - selection - index = { term . index } data - is - focusable = { ! term . disabled && true } > < span className = { styles . pickedTermText } > { term . item . name } < /span> { ! term . disabled && ( < span className = { styles . pickedTermCloseIcon } onClick = { term . onRemoveItem } > < Icon iconName = "Cancel" / > < /span>)} < /div>); } /**
     * Renders the suggestions in the picker
     */ protected onRenderSuggestionsItem ( term ) { let termParent = term . termSetName ; let termTitle = `${ term . name } [${ term . termSetName }]` ; if ( term . path . indexOf ( ";" ) !== - 1 ) { let splitPath = term . path . split ( ";" ) ; termParent = splitPath [ splitPath . length - 2 ] ; splitPath . pop ( ) ; termTitle = `${ term . name } [${ term . termSetName }:${ splitPath . join ( ':' ) }]` ; } return ( < div className = { styles . termSuggestion } title = { termTitle } > < div > { term . name } < /div> < div className = { styles . termSuggestionSubTitle } > { strings . TaxonomyPickerInLabel } { termParent ? termParent : strings . TaxonomyPickerTermSetLabel } < /div> < /div>); } /**
     * When Filter Changes a new search for suggestions
     */ private async onFilterChanged ( filterText , tagList ) { if ( filterText !== "" ) { const { termPickerHostProps , context , isTermSetSelectable } = this . props ; let terms = await this . termsService . searchTermsByName ( filterText ) ; if ( isTermSetSelectable && ! termPickerHostProps . anchorId ) { const termSet = await this . termsService . getTermSet ( ) ; if ( termSet && termSet . Name . toLowerCase ( ) . indexOf ( filterText . toLowerCase ( ) ) === 0 ) { terms . push ( { key : this . termsService . cleanGuid ( termSet . Id ) , name : termSet . Name , path : "" , termSet : this . termsService . cleanGuid ( termSet . Id ) } ) ; } } const filteredTerms = [ ] ; const { disabledTermIds , disableChildrenOfDisabledParents } = this . props ; for ( const term of terms ) { let canBePicked = true ; if ( disabledTermIds && disabledTermIds . length > 0 ) { if ( disabledTermIds . indexOf ( term . key ) !== - 1 ) { canBePicked = false ; } else { if ( disableChildrenOfDisabledParents ) { if ( ! this . allTerms ) { this . allTerms = await this . termsService . getAllTerms ( this . props . termPickerHostProps . termsetNameOrID ) ; } if ( this . allTerms . Terms && this . allTerms . Terms . length > 0 ) { const disabledParents = this . allTerms . Terms . filter ( t => disabledTermIds . indexOf ( t . Id ) !== - 1 ) ; if ( disabledParents && disabledParents . length > 0 ) { const findTerm = disabledParents . filter ( pt => term . path . indexOf ( pt . PathOfTerm ) !== - 1 ) ; if ( findTerm && findTerm . length > 0 ) { canBePicked = false ; } } } } } } if ( canBePicked ) { if ( tagList . filter ( tag => tag . key === term . key ) . length === 0 ) { filteredTerms . push ( term ) ; } } } return filteredTerms ; } else { return Promise . resolve ( [ ] ) ; } } /**
     * gets the text from an item
     */ private onGetTextFromItem ( item ) { return item . name ; } /**
     * Render method
     */ public render ( ) { const { disabled , value , onChanged , onInputChange , onBlur , onNewTerm , allowMultipleSelections , placeholder } = this . props ; const { terms } = this . state ; const clearDisplayValue = ( ) => { const picker = this . state . elRef as unknown as TermBasePicker ; const autoFill = picker ?. [ 'input' ] ?. current as Autofill ; if ( autoFill ) { autoFill [ '_value' ] = '' ; autoFill . setState ( { displayValue : '' } ) ; } else { throw new Error ( `TermPicker.TermBasePicker.render.clearDisplayValue no autoFill to reset displayValue` ) ; } } ; const inputProps = { placeholder : placeholder } ; if ( onNewTerm ) { inputProps . onKeyDown = ( e ) => { if ( e && e . key === 'Enter' && ( ! ( e . ctrlKey || e . altKey || e . shiftKey ) ) && e . target ?. [ 'value' ] ) { onNewTerm ( e . target [ 'value' ] ) ; clearDisplayValue ( ) ; } } ; } return ( < div > < TermBasePicker ref = { ( elRef ) => { if ( ! this . state . elRef ) { this . setState ( { elRef } ) ; } } } disabled = { disabled } onResolveSuggestions = { this . onFilterChanged } onRenderSuggestionsItem = { this . onRenderSuggestionsItem } getTextFromItem = { this . onGetTextFromItem } onRenderItem = { this . onRenderItem } defaultSelectedItems = { value } selectedItems = { terms } onChange = { onChanged } onInputChange = { onInputChange } onBlur = { onBlur } itemLimit = { ! allowMultipleSelections ? 1 : undefined } className = { styles . termBasePicker } inputProps = { inputProps } / > < /div>); } }