azure-devops-ui
Version:
React components for building web UI in Azure DevOps
1 lines • 11.1 kB
JavaScript
import"../../CommonImports";import"../../Core/core.css";import"./TagPicker.css";import*as React from"react";import{ObservableArray,ObservableLike,ObservableValue}from"../../Core/Observable";import{TimerManagement}from"../../Core/TimerManagement";import{Callout}from"../../Callout";import{FocusWithin}from"../../FocusWithin";import{FormItemContext}from"../../FormItem";import{Icon,IconSize}from"../../Icon";import{Measure}from"../../Measure";import{Observer}from"../../Observer";import{Pill}from"../../Pill";import{SuggestionsList}from"../../SuggestionsList";import{css,getSafeId,KeyCode}from"../../Util";import{Location}from"../../Utilities/Position";class TagPicker extends React.Component{constructor(e){super(e),this.inputElement=React.createRef(),this.outerElement=React.createRef(),this.textValue=new ObservableValue(""),this.suggestionsVisible=new ObservableValue(!1),this.selectedIndex=new ObservableValue(-1),this.selectableTags=new ObservableArray([]),this.isTagPickerAccessibilityFixEnabled=!1,this.clearTagPicker=()=>{this.suggestionsVisible.value=!1,this.textValue.value="",this.selectedIndex.value=-1},this.suggestionsLoaded=()=>(-1===this.selectedIndex.value&&!ObservableLike.getValue(this.props.suggestionsLoading)&&this.inputElement.current&&""!==this.inputElement.current.value&&(this.updateIndexTimer&&window.cancelAnimationFrame(this.updateIndexTimer),this.updateIndexTimer=window.requestAnimationFrame(()=>{this.selectedIndex.value=0})),!0),this.createGenericItem=(e,t)=>{if(0!==e.trim().length){const s=this.props.createDefaultItem&&this.props.createDefaultItem(e);if(!s||!t.some(e=>this.props.areTagsEqual(s,e)))return s}},this.onBlur=()=>{var e;this.props.shouldBlurClear&&!this.props.shouldBlurClear()||(e=this.textValue.value,this.textValue.value="",this.selectableTags.value=[],this.props.onBlur&&this.props.onBlur(e),this.onSuggestionsDismiss())},this.onFocus=()=>{this.props.onFocus&&this.props.onFocus()},this.onOuterKeyDown=e=>{switch(e.which){case KeyCode.delete:case KeyCode.backspace:!e.isDefaultPrevented()&&0<this.selectableTags.value.length&&(this.props.onTagsRemoved&&this.props.onTagsRemoved(this.selectableTags.value),this.selectableTags.value=[],this.focusInput(),e.preventDefault())}},this.onKeyDown=e=>{var t,s=e.which,i=this.inputElement.current,a=this.suggestionsVisible.value,l=ObservableLike.getValue(this.props.suggestions);switch(s){case KeyCode.escape:this.onSuggestionsDismiss(),e.preventDefault();break;case KeyCode.tab:case KeyCode.enter:e.shiftKey||(a?(this.completeSuggestion(),e.preventDefault()):this.props.createDefaultItem&&(t=this.createGenericItem(this.textValue.value,ObservableLike.getValue(this.props.selectedTags)))&&(this.addItem(t),e.preventDefault()));break;case KeyCode.upArrow:a&&(this.selectedIndex.value=Math.max(0,this.selectedIndex.value-1),e.preventDefault());break;case KeyCode.downArrow:a?this.selectedIndex.value=Math.min(l.length-1,this.selectedIndex.value+1):(""===this.textValue.value&&this.props.onEmptyInputFocus&&this.props.onEmptyInputFocus(),this.suggestionsVisible.value=!0),e.preventDefault();break;case KeyCode.rightArrow:a&&l&&l[this.selectedIndex.value]&&this.props.onSuggestionExpanded&&i&&i.value.length===i.selectionEnd&&(this.props.onSuggestionExpanded(l[this.selectedIndex.value]),e.preventDefault())}},this.onInputClick=e=>{this.props.onEmptyInputFocus&&""===this.textValue.value&&this.props.onEmptyInputFocus(),this.suggestionsVisible.value=!0,e&&e.preventDefault()},this.onInputChange=e=>{this.textValue.value=e.target.value,this.selectedIndex.value=-1,this.onResolveInput(e),e.persist(),e.preventDefault()},this.onResolveInput=e=>{var t=this.props.deliminator&&this.textValue.value.split(this.props.deliminator);this.props.onDelimitedSearch&&this.props.deliminator&&t&&1<t.length?this.props.onDelimitedSearch(this.textValue.value.split(this.props.deliminator).filter(e=>""!==e)):(this.props.onSearchChanged(e.target.value),this.suggestionsVisible.value=!0)},this.onTagClicked=(e,t)=>{var s;e&&e.isDefaultPrevented()||(s=this.indexOfTag(t,this.selectableTags.value),this.props.onTagsRemoved&&(s<0?this.selectableTags.push(t):this.selectableTags.splice(s,1),e)&&e.preventDefault())},this.onTagRemoved=e=>{var t=this.indexOfTag(e,this.selectableTags.value);0<=t&&this.selectableTags.splice(t,1),this.props.onTagRemoved(e),this.inputElement.current&&this.inputElement.current.focus()},this.onTagPickerSizeChanged=(e,t)=>{this.setState({width:e})},this.completeSuggestion=()=>{var e=ObservableLike.getValue(this.props.suggestions)[this.selectedIndex.value];e&&this.addItem(e)},this.onSuggestionClick=e=>{this.addItem(e.item)},this.addItem=e=>{this.suggestionsVisible.value=!1,this.props.onTagAdded(e),this.textValue.value="",this.selectedIndex.value=-1,this.focusInput()},this.focusInput=()=>{this.inputElement.current&&(this.inputElement.current.focus(),this.inputElement.current.select())},this.onSuggestionsDismiss=()=>{this.suggestionsVisible.value=!1},this.indexOfTag=(t,e)=>e.findIndex(e=>this.props.areTagsEqual(e,t)),this.onAddButtonClicked=()=>{requestAnimationFrame(()=>{var e;this.outerElement.current&&((e=this.outerElement.current).scrollTop=e.scrollHeight)})},this.state={width:296},this.timerManagement=new TimerManagement,this.isTagPickerAccessibilityFixEnabled="undefined"!=typeof document&&(null==(e=document.body)?void 0:e.classList.contains("tag-picker-accessibility-fix-enabled"))}render(){const{ariaLabel:o,ariaLabelledBy:r,className:t,convertItemToPill:c,noResultsFoundText:s,onTagsRemoved:u,placeholderText:g,prefixIconProps:i,renderSuggestionItem:a,selectedTags:e,suggestions:l,suggestionsLoading:d,suggestionsLoadingText:h,suggestionsContainerAriaLabel:p}=this.props;return React.createElement(FocusWithin,{onBlur:this.onBlur,onFocus:this.onFocus},n=>React.createElement(React.Fragment,null,React.createElement(Observer,{suggestionsLoading:{observableValue:d,filter:this.suggestionsLoaded},suggestionsVisible:this.suggestionsVisible,selectedIndex:this.selectedIndex,selectableTags:this.selectableTags,selectedTags:e,suggestions:{observableValue:l,filter:this.suggestionsLoaded},textValue:this.textValue},l=>{var e=this.createGenericItem(this.textValue.value,l.suggestions.concat(l.selectedTags));return e&&l.suggestions.unshift(e),React.createElement(Measure,{onMeasure:this.onTagPickerSizeChanged},React.createElement("div",{className:css("bolt-tag-picker",(n.hasFocus||l.suggestionsVisible)&&"edit",t),ref:this.outerElement,onBlur:n.onBlur,onFocus:n.onFocus,onClick:this.focusInput},React.createElement("div",{className:"bolt-tag-picker-group flex-center flex-row flex-grow flex-wrap",onKeyDown:u&&this.onOuterKeyDown},0===l.selectedTags.length&&i?React.createElement(Icon,Object.assign({},i,{className:css(i.className,"bolt-tag-picker-prefix-icon")})):null,!this.isTagPickerAccessibilityFixEnabled&&l.selectedTags.map((t,e)=>{const s=c(t,e);var i=this.indexOfTag(t,l.selectableTags);return React.createElement(Pill,Object.assign({},s,{key:getSafeId("bolt-tag-picker-pill"+e),className:css(s.className,"bolt-tag-picker-pill",u&&"bolt-tag-picker-pill-selectable",0<=i&&"active"),contentClassName:"text-ellipsis scroll-hidden",onClick:e=>{this.onTagClicked(e,t),s.onClick&&s.onClick(e)},onRemoveClick:e=>{this.onTagRemoved(t),e.preventDefault()}}),s.content)}),React.createElement(FormItemContext.Consumer,null,e=>{var t=0===l.selectedTags.length||n.hasFocus?g:void 0,s=!n.hasFocus&&0<l.selectedTags.length&&!l.suggestionsVisible;let i;l.suggestionsVisible&&(i=-1===l.selectedIndex||l.suggestionsLoading?getSafeId("sug-list-transition"):l.suggestions&&l.suggestions.length?getSafeId("sug-row-"+l.selectedIndex):getSafeId("sug-list-no-results"));var a=l.suggestionsVisible?getSafeId("suggestion-list"):void 0;return this.isTagPickerAccessibilityFixEnabled?React.createElement(React.Fragment,null,React.createElement("div",{role:"list","aria-labelledby":getSafeId(e.ariaLabelledById),className:"tag-picker-accessibility-fix-enabled flex-row flex-wrap"},l.selectedTags.map((t,e)=>{const s=c(t,e);var i=this.indexOfTag(t,l.selectableTags);return React.createElement(Pill,Object.assign({},s,{isListItem:!0,key:getSafeId("bolt-tag-picker-pill"+e),className:css(s.className,"bolt-tag-picker-pill",u&&"bolt-tag-picker-pill-selectable",0<=i&&"active"),contentClassName:"text-ellipsis scroll-hidden",onClick:e=>{this.onTagClicked(e,t),s.onClick&&s.onClick(e)},onRemoveClick:e=>{this.onTagRemoved(t),e.preventDefault()}}),s.content)})),React.createElement("div",{className:"bolt-tag-picker-add-icon-div flex-row flex-grow"},s&&React.createElement(Icon,{className:"bolt-tag-picker-add-icon cursor-pointer",iconName:"Add",size:IconSize.small,onClick:this.onAddButtonClicked}),React.createElement("input",{"aria-activedescendant":i,"aria-autocomplete":"list","aria-controls":a,"aria-expanded":l.suggestionsVisible,"aria-haspopup":"listbox","aria-label":o||g,"aria-labelledby":getSafeId(r||e.ariaLabelledById),className:css("bolt-tag-picker-input flex-row flex-grow scroll-hidden",s&&!t&&"hide-input"),onBlur:n.onBlur,onChange:this.onInputChange,onKeyDown:this.onKeyDown,onClick:this.onInputClick,placeholder:t,ref:this.inputElement,role:"combobox",type:"text",value:l.textValue}))):React.createElement("div",{className:"bolt-tag-picker-add-icon-div flex-row flex-grow"},s&&React.createElement(Icon,{className:"bolt-tag-picker-add-icon cursor-pointer",iconName:"Add",size:IconSize.small,onClick:this.onAddButtonClicked}),React.createElement("input",{"aria-activedescendant":i,"aria-autocomplete":"list","aria-controls":a,"aria-expanded":l.suggestionsVisible,"aria-haspopup":"listbox","aria-label":o||g,"aria-labelledby":getSafeId(r||e.ariaLabelledById),className:css("bolt-tag-picker-input flex-row flex-grow scroll-hidden",s&&!t&&"hide-input"),onBlur:n.onBlur,onChange:this.onInputChange,onKeyDown:this.onKeyDown,onClick:this.onInputClick,placeholder:t,ref:this.inputElement,role:"combobox",type:"text",value:l.textValue}))}))))}),React.createElement(Observer,{suggestionsVisible:this.suggestionsVisible,suggestionsLoading:d,selectedIndex:this.selectedIndex,suggestions:l,textValue:this.textValue},e=>e.suggestionsVisible?React.createElement(Callout,{anchorElement:this.outerElement.current||void 0,anchorOrigin:{horizontal:Location.start,vertical:Location.end},calloutOrigin:{horizontal:Location.start,vertical:Location.start},contentClassName:"bolt-tag-picker-callout-content scroll-hidden",contentShadow:!0,id:"tag-picker-callout",role:"presentation"},React.createElement(SuggestionsList,{isLoading:e.suggestionsLoading,loadingText:h,onBlur:n.onBlur,onFocus:n.onFocus,noResultsFoundText:e.textValue?s:void 0,onSuggestionClicked:this.onSuggestionClick,renderSuggestion:a,selectedIndex:e.selectedIndex,suggestions:e.suggestions,suggestionsContainerAriaLabel:p,width:this.state.width})):null)))}componentDidMount(){this.onResolveInput=this.timerManagement.debounce(this.onResolveInput,this.props.onSearchChangedDebounceWait)}focus(){this.inputElement.current&&this.inputElement.current.focus()}}TagPicker.defaultProps={onSearchChangedDebounceWait:250};export{TagPicker};