table-filter-multiselect
Version:
## Install You need to have react and react-dom as dependencies in your project.
393 lines (345 loc) • 11.4 kB
JavaScript
import React from 'react';
import FilterListItem from './filterListItem';
import SelectAllItem from './selectAllItem';
import SearchBar from './searchBar';
import {
isUndefined,
isTypeString,
getValForKey
} from './lib/util';
import EventStack from './lib/eventStack';
import FilterIcon from './filterIcon';
import {
sortAction
} from './lib/sort';
import {
BLANK_LABEL,
ASC_VALUE,
DSC_VALUE
} from './lib/constants';
import SortIcon from './sortIcon';
import PropTypes from 'prop-types';
class FilterList extends React.Component {
constructor(props){
super(props);
this._initMethods();
const { filterList, selectState } = this._calculateFilterState(this.props.filteredData);
this.appliedSearchFilters = undefined;
this.searchValue = undefined;
this.state = {
filterList: filterList,
showFilter: false,
selectAllFilters: selectState,
sortType: undefined,
searchEnabled: false,
clearClicked: props.clearClicked,
isUpdated: false
};
}
/*Bind functions to react context*/
_initMethods(){
this._handleOutsideClick = this._handleOutsideClick.bind(this);
this._calculateFilterState = this._calculateFilterState.bind(this);
this._filterIconClicked = this._filterIconClicked.bind(this);
this._displayFilter = this._displayFilter.bind(this);
this._hideFilter = this._hideFilter.bind(this);
this._filterUpdated = this._filterUpdated.bind(this);
this._selectAllClicked = this._selectAllClicked.bind(this);
this._filterData = this._filterData.bind(this);
this._resetData = this._resetData.bind(this);
this._sortClicked = this._sortClicked.bind(this);
this._searchChanged = this._searchChanged.bind(this);
}
componentWillUnmount(){
/*Remove body click listener*/
EventStack.unsub('click', this._handleOutsideClick, document);
}
/**
* [_handleOutsideClick Function to hide open filter list when click is done anywhere else]
*/
_handleOutsideClick(e){
if(!this.filterIconNode.contains(e.target)){
this._hideFilter();
}
}
componentWillReceiveProps(nextProps){
const { filterList, selectState } = this._calculateFilterState(nextProps.filteredData);
const sortTypeState = (!isUndefined(nextProps.sortKey) && (nextProps.sortKey === this.props.filterkey) ) ? nextProps.sortType : undefined;
this.setState({
filterList: filterList,
selectAllFilters: selectState,
sortType: sortTypeState
});
}
/**
* [_calculateFilterState Function calculates current filter state to display]
* @param {Array} dataArray [Data passed from parent on which filter is to be applies]
*/
_calculateFilterState(dataArray){
let filteredData = dataArray ? [...dataArray] : [];
const filterkey = this.props.filterkey;
let usedKeys = [];
let filteredUsedKeys = [];
let filterList = [];
let selectState = true;
filteredData.map((item, index) => {
let itemKey = getValForKey(item, filterkey)
let orinigalValue = itemKey;
if(!isUndefined(this.props.itemDisplayValueFunc)){
itemKey = this.props.itemDisplayValueFunc(itemKey);
}
if(this.props.clearClicked == true){
item.appliedFilters = {}
}
var appliedFilters = item.appliedFilters || {};
let displayName = itemKey;
if(isUndefined(itemKey)){
displayName = BLANK_LABEL;
itemKey = "";
orinigalValue = displayName;
}else if((isTypeString(itemKey))){
itemKey = itemKey.trim();
if(itemKey.length === 0){
displayName = BLANK_LABEL;
orinigalValue = displayName;
}
}
// if((isTypeString(itemKey)) && !isUndefined(itemKey) && itemKey.length != 0) {
// itemKey = itemKey.trim();
// }
if(usedKeys.indexOf(itemKey) === -1){
if((!isUndefined(appliedFilters) && Object.keys(appliedFilters).length > 0)){
if(Object.keys(appliedFilters).length === 1 && Object.keys(appliedFilters)[0] === filterkey){
selectState = false;
filterList.push({
"key": itemKey,
"display": displayName,
"selected": false,
"visible": true,
"orinigalValue": orinigalValue
});
}else{
filterList.push({
"key": itemKey,
"display": displayName,
"selected": true,
"visible": false,
"orinigalValue": orinigalValue
});
}
}else{
filterList.push({
"key": itemKey,
"display": displayName,
"selected": true,
"visible": true,
"orinigalValue": orinigalValue
});
}
usedKeys.push(itemKey);
}else{
const filterIndex = usedKeys.indexOf(itemKey);
let filterItem = filterList[filterIndex];
if(Object.keys(appliedFilters).length === 0){
if(!filterItem.selected || !filterItem.visible){
filterItem = Object.assign({}, filterItem, {"selected": true, "visible": true});
filterList[filterIndex] = filterItem;
}
}
if(Object.keys(appliedFilters).length === 1 && Object.keys(appliedFilters)[0] === filterkey){
selectState = false;
filterItem = Object.assign({}, filterItem, {"selected": false, "visible": true});
filterList[filterIndex] = filterItem;
}
}
});
filterList = sortAction(filterList, ASC_VALUE, {
valueFunc: this.props.itemSortValueFunc,
key: 'orinigalValue'
});
return {
filterList,
selectState
};
}
_filterIconClicked(e){
const filterState = this.state.showFilter;
const newState = !filterState;
if(newState){
this._displayFilter();
}else{
this._hideFilter();
}
}
_displayFilter(){
/*Body Click listener added*/
EventStack.sub('click', this._handleOutsideClick, document);
this.setState({
showFilter: true
});
}
_hideFilter(){
/*Body Click listener removed*/
EventStack.unsub('click', this._handleOutsideClick, document);
this.setState({
showFilter: false,
searchEnabled: false
});
}
/**
* [_filterUpdated method called when a filter list item is clicked]
* @param {Number} index [Index of the filter clicked]
*/
_filterUpdated(index){
let allFilters = this.state.filterList;
if(!isUndefined(allFilters[index])){
const newFilterState = !allFilters[index]['selected'];
this._filterData(allFilters[index]['key'], !newFilterState);
}
}
/**
* [_selectAllClicked method called when a select all item is clicked]
*/
_selectAllClicked(){
const selectAllState = this.state.selectAllFilters;
const newSelectAllState = !selectAllState;
const searchState = this.state.searchEnabled;
// const searchValue = this.searchValue;
if(searchState){
return;
}
const visibleFiltersValues = this.state.filterList.filter((filterItem) => {
if(newSelectAllState){
return (filterItem.visible && !filterItem.selected);
}else{
return (filterItem.visible && filterItem.selected);
}
}).map((filterItem) => {
return filterItem.key;
});
this._resetData(visibleFiltersValues, newSelectAllState);
}
/**
* [_filterData Method calls parent props filter mehtod when filters are changed]
* @param {String} filterValue [Filter value]
* @param {Boolean} addFilter [Add/Remove]
*/
_filterData(filterValue=undefined, addFilter=true){
this.props.filterRows(filterValue, this.props.filterkey, addFilter, this.props.itemDisplayValueFunc);
}
/**
* [_resetData Method calls parent props reset mehtod]
* @param {Array} filterValues [Array of filter values]
* @param {Boolean} selectAll [Add/Remove]
*/
_resetData(filterValues=[], selectAll=true){
this.props.resetRows(filterValues, this.props.filterkey, selectAll, this.props.itemDisplayValueFunc);
}
/**
* [_sortClicked description]
* @return {[type]} [description]
*/
_sortClicked(){
const currentSortType = this.state.sortType;
let newSortType;
if(isUndefined(currentSortType) || currentSortType === DSC_VALUE){
newSortType = ASC_VALUE;
}else{
newSortType = DSC_VALUE;
}
this.props.sortRows(newSortType, {
itemSortValueFunc: this.props.itemSortValueFunc,
caseSensitive: this.props.caseSensitive,
key: this.props.filterkey
});
}
/**
* [_searchChanged Method called when search is triggered]
* @param {String} searchValue [Search value]
*/
_searchChanged(searchValue){
let searchState = false;
let propKey = this.props.filterkey;
this.searchValue = searchValue;
const prevAppliedFilters = this.appliedSearchFilters;
if(!isUndefined(searchValue, true)){
this.setState({
searchEnabled: true
});
searchValue = searchValue.toLowerCase();
let filterList = this.state.filterList;
searchState = true;
const filtersToApply = filterList.filter((filterItem) => {
const filterKey = filterItem.key.toString().toLowerCase();
if(filterKey.indexOf(searchValue) < 0 && filterItem.visible){
return true;
}
return false;
}).map((filterItem) => {
return {
key: propKey,
value: filterItem.key
}
});
this.appliedSearchFilters = filtersToApply;
this.props.filterMultipleRows(filtersToApply, prevAppliedFilters, this.props.itemDisplayValueFunc);
}else{
this.setState({
searchEnabled: false
});
this.appliedSearchFilters = [];
this.props.filterMultipleRows([], prevAppliedFilters, this.props.itemDisplayValueFunc);
}
}
render(){
const filterState = this.state.showFilter,
filterkey = this.props.filterkey,
showSearch = this.props.showSearch === false ? false : true;
let filterListItemHtml = [],
filterListHtml;
// if(this.state.filterList.length > 1){
if(filterState){
const searchBarHtml = showSearch ? <SearchBar searchChanged={ this._searchChanged }/> : null;
this.state.filterList.map((filterItem, index) => {
if(filterItem.visible){
if(this.state.searchEnabled){
const filterKey = filterItem.key.toString().toLowerCase();
if(filterKey.indexOf(this.searchValue.toLowerCase()) >= 0){
return filterListItemHtml.push(<FilterListItem key={"item_"+index} filterClicked={this._filterUpdated} index={index} label={filterItem.display} selected={filterItem.selected} />);
}else{
return null;
}
}else{
filterListItemHtml.push(<FilterListItem key={"item_"+index} filterClicked={this._filterUpdated} index={index} label={filterItem.display} selected={filterItem.selected} />);
}
}
});
const filterListClass = [ (this.props.alignleft === true) ? "align-left " : "", "filter-list"].join('');
filterListHtml = (<div className={ filterListClass }>
{ searchBarHtml }
<SortIcon sort={ this._sortClicked } sortType={ this.state.sortType }/>
<SelectAllItem filterClicked={this._selectAllClicked} selected={this.state.selectAllFilters}/>
{ filterListItemHtml }
</div>);
}
const filterActive = !this.state.selectAllFilters || filterState;
return (<div className="table-filter-parent" ref={ (node) => {this.filterIconNode = node;} }>
<FilterIcon iconClicked={ this._filterIconClicked } childId={this.props.childId} selected={filterActive}/>
{ filterListHtml }
</div>);
// }else{
// return (<div style={ {display: "none"} }></div>);
// }
}
}
FilterList.propTypes = {
initialData: PropTypes.array.isRequired,
filteredData: PropTypes.array.isRequired,
filterRows: PropTypes.func.isRequired,
resetRows: PropTypes.func.isRequired,
sortRows: PropTypes.func.isRequired,
sortKey: PropTypes.string,
sortType: PropTypes.string,
clearClicked: PropTypes.bool
}
export default FilterList