azure-devops-ui
Version:
React components for building web UI in Azure DevOps
1 lines • 15 kB
JavaScript
import"../../CommonImports";import"../../Core/core.css";import"./Filter.css";import*as React from"react";import{ObservableValue}from"../../Core/Observable";import{TimerManagement}from"../../Core/TimerManagement";import{announce}from"../../Core/Util/Accessibility";import{ScreenSize}from"../../Core/Util/Screen";import{format}from"../../Core/Util/String";import{Button}from"../../Button";import{ContentLocation}from"../../Callout";import{Dropdown,DropdownCalloutComponent,DropdownExpandableButton,filterItems}from"../../Dropdown";import{FocusZoneContext}from"../../FocusZone";import{Icon}from"../../Icon";import{renderListCell}from"../../List";import{getListBoxItemsValue,ListBox,ListBoxItemType,wrapListBoxItems}from"../../ListBox";import{Observer,SelectionObserver,UncheckedObserver}from"../../Observer";import{Pill,PillSize}from"../../Pill";import*as Resources from"../../Resources.Filter";import{TextField}from"../../TextField";import{css,KeyCode}from"../../Util";import{updateFilterToSelection}from"../../Utilities/DropdownFilter";import{DropdownMultiSelection}from"../../Utilities/DropdownSelection";import{FILTER_CHANGE_EVENT}from"../../Utilities/Filter";import{Location}from"../../Utilities/Position";import{ScreenSizeObserver}from"../../Utilities/ScreenSize";import{compareSelectionRanges,indexWithinRanges}from"../../Utilities/Selection";import*as Utils_Accessibility from"../../Core/Util/Accessibility";const FilterCalloutWidth=320,FilterItemPadding=48;class Filter extends React.Component{constructor(e){super(e),this.dropdown=React.createRef(),this.dropdownCallout=React.createRef(),this.filterText=new ObservableValue(""),this.timerManagement=new TimerManagement,this.collapse=()=>{this.dropdown.current&&this.dropdown.current.collapse()},this.expand=()=>{this.dropdown.current&&this.dropdown.current.expand()},this.onDoneClick=()=>{var e=this.props["filterStore"];e.usesApplyMode()&&e.applyChanges(),this.collapse()},this.onApplyClick=()=>{var e=this.props["filterStore"];e.usesApplyMode()&&e.applyChanges(),this.clearActiveFilter()},this.onExpandClick=()=>{Utils_Accessibility.announce(this.props.title||Resources.FilterTitle)},this.renderBeforeContent=()=>React.createElement(Observer,{activeFilter:this.activeFilter,filterText:this.filterText,bestHitItem:this.props.bestHitItem,userFilteredItems:this.props.userFilteredItems},e=>e.activeFilter?e.activeFilter.renderBeforeContent?e.activeFilter.renderBeforeContent(this.clearActiveFilter):null:e.filterText?this.renderFilteredView():this.renderFilterItems()),this.renderFilteredView=()=>{const r=[];this.props.bestHitItem&&this.props.bestHitItem.value&&(r.push({id:"best-hit-header",text:Resources.BestHit,type:ListBoxItemType.Header,className:"bolt-filtered-header"}),r.push(this.props.bestHitItem.value)),this.props.userFilteredItems?r.push(...getListBoxItemsValue(this.props.userFilteredItems)):(this.props.filterItems.forEach(t=>{let e=filterItems(getListBoxItemsValue(t.items),this.filterText.value||"").filteredItems;const i=this.getSelectedFilterItems(t);(e=e.filter(e=>e.type!==ListBoxItemType.Header&&e.type!==ListBoxItemType.Divider&&-1===i.indexOf(e)&&(!this.props.bestHitItem||this.props.bestHitItem.value!==e))).length&&(r.push({id:t.id,text:t.name,type:ListBoxItemType.Header,className:"bolt-filtered-header"}),r.push(...e.map(e=>Object.assign(Object.assign({},e),{groupId:t.id}))))}),this.props.filterItems.some(e=>"keyword-item"===e.id)&&r.push(...getKeywordSearchResults(this.filterText.value)));var e=r.filter(e=>e.type!==ListBoxItemType.Header).length;return 0<e?this.announceWithDebouncing(format(Resources.AnnounceFilterResultCount,e)):this.announceWithDebouncing(Resources.NoFilterResults),React.createElement(ListBox,{items:r,onSelect:this.onFilteredItemSelect,onActivate:this.onFilteredItemSelect,excludeTabStop:!0,focuszoneProps:null})},this.renderSelectedItems=(e,t)=>!1!==this.props.showFilterOnText&&this.filtered()?Resources.FilterOn:Resources.Filter,this.renderFilterItems=()=>{const{filterItems:e,filterStore:n}=this.props;return React.createElement(FocusZoneContext.Consumer,null,o=>e.map((t,e)=>{var i=this.getSelectedFilterItems(t),r=i.length,l=n.getFilterItemState(t.filterItemKey),s=n.getDefaultState()[t.filterItemKey],s=n.filterItemStatesAreEqual(t.filterItemKey,l,s);return React.createElement("div",{className:"flex-row flex-center bolt-filter-item",key:t.id,id:"bolt-filter-item-"+t.id,"data-focuszone":o.focuszoneId,tabIndex:-1,onClick:()=>this.onFilterItemSelected(t),onKeyDown:e=>{e.defaultPrevented||e.which!==KeyCode.enter&&e.which!==KeyCode.space&&e.which!==KeyCode.rightArrow||(this.onFilterItemSelected(t,e.currentTarget),e.preventDefault())}},React.createElement("div",{className:css("flex-row flex-center flex-grow bolt-filter-label",l&&l.value&&0!==l.value.length&&"bolt-filter-label-selected"),style:{width:this.props.width-FilterItemPadding}},React.createElement("span",{className:css(s&&"primary-text",!s&&"font-weight-semibold")},t.name),1<r&&React.createElement(Pill,{className:"bolt-filter-selection-pill",excludeFocusZone:!0,size:PillSize.compact},r),React.createElement("div",{className:"flex-grow flex-row bolt-filter-selected-item-container"},t.renderSelectedItems?t.renderSelectedItems(i):renderSelectedFilterItems(i))),React.createElement(Icon,{iconName:"ChevronRight"}))}))},this.onFilteredItemSelect=(e,t)=>{var i,r,l,s;t.groupId&&(i=this.props["filterStore"],r=this.props.filterItems.find(e=>e.id===t.groupId))&&(r=r.filterItemKey,l=i.getFilterItemState(r),s=void 0!==t.data?t.data:t.id,"keyword"===r?i.setFilterItemState(r,{value:t.id}):l&&l.value&&Array.isArray(l.value)&&this.selection.multiSelect?i.setFilterItemState(t.groupId,{value:[...l.value,s]}):i.setFilterItemState(r,{value:[s]})),this.filterText.value="",this.dropdownCallout.current&&this.dropdownCallout.current.focus()},this.onFilterChanged=e=>{var t=this.props.filterStore.getState(),i=new DropdownMultiSelection,r=getListBoxItemsValue(this.wrappedItems||this.props.items);for(const o in t){const n=t[o];if(n&&n.value)if("keyword"===o){var l=r.findIndex(e=>e.id===n.value);-1<l&&i.select(l,1,!0)}else for(let t=0;t<n.value.length;t++){var s=r.findIndex(e=>e.id===n.value[t]||e.data===n.value[t]);-1<s&&i.select(s,1,!0)}}compareSelectionRanges(this.selection.value,i.value).length&&(this.selection.value=i.value)},this.onSelectionChanged=e=>{var t=getListBoxItemsValue(this.wrappedItems||this.props.items);if(this.props.filterStore&&this.activeFilter.value){const r=new DropdownMultiSelection;let i=0;for(let e=0;this.props.filterItems[e].id!==this.activeFilter.value.id;e++)i+=this.props.filterItems[e].items.length;if(e.forEach(t=>{for(let e=t.beginIndex;e<=t.endIndex;e++)e>=i&&e<i+this.activeFilter.value.items.length&&r.select(e,1,!0)}),"keyword"===this.activeFilter.value.filterItemKey&&0===r.value.length)return!0;updateFilterToSelection(r.value,t,this.props.filterStore,this.activeFilter.value.filterItemKey)}return!0},this.onResetClick=()=>{this.dropdownCallout.current&&this.dropdownCallout.current.focus(),this.props.filterStore.reset()},this.onResetFilterItemClick=e=>{this.dropdownCallout.current&&this.dropdownCallout.current.focus(),this.props.filterStore.resetFilterItemState(e)},this.onFilterItemSelected=(e,t)=>{this.dropdownCallout.current&&this.dropdownCallout.current.focus(),this.props.activeFilter||(this.activeFilter.value=e,announce(format(Resources.FilterSelected,e.name)),this.activeFilterReturnElementId=null==t?void 0:t.id),this.props.onActiveFilterChanged&&this.props.onActiveFilterChanged(e)},this.getOnFilterTextChanged=i=>(e,t)=>{this.filterText.value=t,this.activeFilter.value&&i.onFilterTextChanged&&(i.onFilterTextChanged(e,t),this.dropdownOnFilterTextChanged=i.onFilterTextChanged),this.props.onFilterTextChanged&&this.props.onFilterTextChanged(e,t)},this.getFilterStartingIndex=e=>{if(e){var i=this.props.filterItems.indexOf(e);let t=0;for(let e=0;e<i;e++)t+=this.props.filterItems[e].items.length;return t}return-1},this.getSelectedFilterItems=t=>{var i=[],r=getListBoxItemsValue(this.wrappedItems||this.props.items),l=this.getFilterStartingIndex(t);for(let e=l;e<l+t.items.length;e++)indexWithinRanges(e,this.selection.value)&&i.push(r[e]);return i},this.clearFilterSelection=()=>{this.activeFilter.value&&(this.props.filterStore.setFilterItemState(this.activeFilter.value.filterItemKey,{value:null}),this.activeFilter.value=null)},this.clearActiveFilter=e=>{e?this.focus():this.dropdownCallout.current&&(this.dropdownCallout.current.focus(),requestAnimationFrame(()=>{var e;this.activeFilterReturnElementId&&(null!=(e=document.getElementById(this.activeFilterReturnElementId))&&e.focus(),this.activeFilterReturnElementId=void 0)})),this.props.activeFilter||(this.activeFilter.value=null),this.props.onActiveFilterChanged&&this.props.onActiveFilterChanged(null),this.filterText.value="",this.dropdownOnFilterTextChanged&&this.dropdownOnFilterTextChanged(null,"")},this.filtered=()=>{var e=this.props.filterStore.getAppliedState();for(const t in e)if(e[t].value&&(!Array.isArray(e[t].value)||0<e[t].value.length))return!0;return!1},this.announceWithDebouncing=e=>{Utils_Accessibility.announce(e,!1,300)},this.state={},this.selection=e.selection||new DropdownMultiSelection,this.wrappedItems=wrapListBoxItems(e.items),this.activeFilter=e.activeFilter||new ObservableValue(null)}focus(){this.dropdown.current&&this.dropdown.current.focus()}componentDidMount(){this.props.filterStore&&this.props.filterStore.subscribe(this.onFilterChanged,FILTER_CHANGE_EVENT),this.onFilterChanged(this.props.filterStore.getState()),this.announceWithDebouncing=this.timerManagement.debounce(this.announceWithDebouncing,300)}componentWillUnmount(){this.props.filterStore&&this.props.filterStore.unsubscribe(this.onFilterChanged,FILTER_CHANGE_EVENT)}render(){const{filterStore:s,showActiveFilterResetButton:o,showFilterOnText:e}=this.props,n=!1!==e&&this.filtered();return React.createElement(UncheckedObserver,{activeFilter:this.activeFilter,filter:s},React.createElement(SelectionObserver,{selection:this.selection,onSelectionChanged:this.onSelectionChanged},()=>{const i=this.activeFilter.value,r=[];let l=0;var e,t={className:"bolt-filter-reset-button",text:i?Resources.Reset:Resources.ResetAll,subtle:!1,onClick:i?()=>this.onResetFilterItemClick(i.filterItemKey):this.onResetClick,id:"filter-reset-button"};return i?(e=s.getFilterItemState(i.filterItemKey),l=this.getSelectedFilterItems(i).length,o?s.hasChangesToReset()&&r.push(t):r.push({text:Resources.Clear,disabled:!(e&&e.value),subtle:!1,onClick:this.clearFilterSelection,id:"filter-clear-button"}),s.usesApplyMode()&&r.push({className:css(!o&&"bolt-filter-apply-button"),disabled:!s.hasChangesToApply(),text:Resources.Apply,primary:!0,subtle:!1,onClick:this.onApplyClick,id:"filter-apply-button"})):(s.hasChangesToReset()&&r.push(t),s.usesApplyMode()&&r.push({disabled:!s.hasChangesToApply(),text:Resources.Apply,primary:!0,subtle:!1,onClick:this.onDoneClick,id:"filter-done-button"})),React.createElement(ScreenSizeObserver,null,e=>{const t=e.screenSize===ScreenSize.xsmall;return React.createElement(Dropdown,{actions:r,calloutContentClassName:css("bolt-filter-callout",i&&"bolt-active-filter",t&&"absolute-fill"),className:css(this.props.className,"bolt-filter",n&&"bolt-filter-on"),dismissOnSelect:!1,enforceSingleSelect:null===i||void 0===i?void 0:i.enforceSingleSelect,filterByText:this.props.filterByText,onExpand:this.onExpandClick,onCollapse:()=>this.clearActiveFilter(!0),placeholder:n?Resources.FilterOn:Resources.Filter,ref:this.dropdown,items:this.props.items,userFilteredItems:i?this.props.userFilteredItems||i.items:[],renderExpandable:e=>React.createElement(DropdownExpandableButton,Object.assign({},e,{iconProps:{iconName:"Filter"},hideDropdownIcon:!0,renderSelectedItems:this.renderSelectedItems})),renderCallout:e=>React.createElement(DropdownCalloutComponent,Object.assign({},e,{ariaLabel:this.props.title||Resources.FilterTitle,anchorElement:t?void 0:e.anchorElement,anchorOrigin:{horizontal:Location.start,vertical:Location.end},blurDismiss:!t,containerClassName:"bolt-filter-listbox-container",contentLocation:t?ContentLocation.Center:void 0,dropdownOrigin:{horizontal:Location.start,vertical:Location.start},enforceSingleSelect:null===i||void 0===i?void 0:i.enforceSingleSelect,filterText:this.filterText,ignoreMouseDown:!0,key:null===i||void 0===i?void 0:i.id,onFilterTextChanged:this.getOnFilterTextChanged(e),onFilterKeyDown:e=>{e&&!e.defaultPrevented&&e.which===KeyCode.enter&&this.props.filterItems.some(e=>"keyword-item"===e.id)&&0<this.filterText.value.length&&(s.setFilterItemState("keyword",{value:this.filterText.value}),this.filterText.value="")},showCloseButton:!0,title:i?React.createElement("div",{className:"flex-row flex-center bolt-filter-title-container"},!this.props.hideBackButton&&React.createElement(Button,{ariaLabel:Resources.Back,subtle:!0,className:"bolt-dropdown-header-button bolt-filter-back-button",iconProps:{iconName:"Back"},onClick:()=>this.clearActiveFilter(),tabIndex:-1}),i.title||i.name,1<l&&React.createElement(Pill,{className:"bolt-filter-selection-pill",size:PillSize.compact},l)):this.props.title||Resources.FilterTitle,renderBeforeContent:this.renderBeforeContent,ref:this.dropdownCallout})),selection:this.selection,showFilterBox:!(i&&!1===i.showFilterBox),width:t?-1:this.props.width})})}))}}function getKeywordFilterItem(r,e,t=[]){const l="keyword";var i=new TimerManagement,s=e=>{r.setFilterItemState(l,{value:e}),r.usesApplyMode()||r.applyChanges()};const o=e?i.debounce(s,e,{leading:!1,trailing:!0}):s;return{items:t,renderBeforeContent:t=>{const i=new ObservableValue("");var e=r.getFilterItemState(l);return i.value=e&&e.value?e.value:"",React.createElement(Observer,{filterExpression:{observableValue:r,filter:()=>{var e=r.getFilterItemState(l);i.value=e&&e.value?e.value:""}}},()=>React.createElement(TextField,{ariaLabel:Resources.Keyword,placeholder:Resources.SearchKeyword,autoFocus:!0,className:"bolt-filter-keyword-item",value:i,onChange:(e,t)=>{i.value=t,o(t)},onKeyDown:e=>{e.which===KeyCode.enter&&(t(),e.preventDefault())}}))},renderSelectedItems:()=>{var e=r.getFilterItemState(l);return e&&e.value?React.createElement("span",null,`"${e.value}"`):null},enforceSingleSelect:!0,id:"keyword-item",filterItemKey:l,name:Resources.Keyword,showFilterBox:!1}}function getKeywordSearchResults(e){var t=[];return t.push({id:"keyword-header",text:Resources.Keyword,type:ListBoxItemType.Header,className:"bolt-filtered-header"}),t.push({id:e,text:format(Resources.KeywordSearchResult,e),groupId:"keyword-item"}),t}function renderSelectedFilterItems(i){const r=i.some(e=>!!e.iconProps);return React.createElement(React.Fragment,null,i.map((e,t)=>React.createElement("div",{className:css("bolt-filter-selected-item flex-row",!r&&"bolt-filter-selected-text-item"),key:e.id},renderListCell(e),!r&&t!==i.length-1&&React.createElement("span",null,", "))))}Filter.defaultProps={width:FilterCalloutWidth};export{Filter,getKeywordFilterItem,getKeywordSearchResults,renderSelectedFilterItems};