implement filter

This commit is contained in:
AllenFang 2017-12-13 21:45:41 +08:00
parent 861809d10c
commit 7016e55472
9 changed files with 241 additions and 6 deletions

View File

@ -0,0 +1,11 @@
{
"name": "react-bootstrap-table2-filter",
"version": "0.0.1",
"description": "it's the column filter addon for react-bootstrap-table2",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,2 @@
export const LIKE = 'LIKE';
export const EQ = '=';

View File

@ -0,0 +1,93 @@
/* eslint react/require-default-props: 0 */
/* eslint react/no-unused-prop-types: 0 */
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import { LIKE, EQ } from '../comparison';
import { FILTER_TYPE, FILTER_DELAY } from '../const';
class TextFilter extends Component {
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.timeout = null;
this.state = {
value: props.defaultValue
};
}
componentDidMount() {
const defaultValue = this.input.value;
if (defaultValue) {
this.props.onFilter(this.props.column, defaultValue, FILTER_TYPE.TEXT);
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.defaultValue !== this.props.defaultValue) {
this.applyFilter(nextProps.defaultValue);
}
}
componentWillUnmount() {
clearTimeout(this.timeout);
}
filter(e) {
e.stopPropagation();
if (this.timeout) {
clearTimeout(this.timeout);
}
const filterValue = e.target.value;
this.setState(() => ({ value: filterValue }));
this.timeout = setTimeout(() => {
this.props.onFilter(this.props.column, filterValue, FILTER_TYPE.TEXT);
}, this.props.delay);
}
cleanFiltered() {
const value = this.props.defaultValue;
this.setState(() => ({ value }));
this.props.onFilter(this.props.column, value, FILTER_TYPE.TEXT);
}
applyFilter(filterText) {
this.setState(() => ({ value: filterText }));
this.props.onFilter(this.props.column, filterText, FILTER_TYPE.TEXT);
}
render() {
const { placeholder, column: { text }, style } = this.props;
// stopPropagation for onClick event is try to prevent sort was triggered.
return (
<input
ref={ n => this.input = n }
type="text"
className="filter text-filter form-control"
style={ style }
onChange={ this.filter }
onClick={ e => e.stopPropagation() }
placeholder={ placeholder || `Enter ${text}...` }
value={ this.state.value }
/>
);
}
}
TextFilter.propTypes = {
onFilter: PropTypes.func.isRequired,
comparator: PropTypes.oneOf([LIKE, EQ]),
defaultValue: PropTypes.string,
delay: PropTypes.number,
placeholder: PropTypes.string,
column: PropTypes.object,
style: PropTypes.object
};
TextFilter.defaultProps = {
delay: FILTER_DELAY,
defaultValue: ''
};
export default TextFilter;

View File

@ -0,0 +1,5 @@
export const FILTER_TYPE = {
TEXT: 'TEXT'
};
export const FILTER_DELAY = 500;

View File

@ -0,0 +1,36 @@
import { FILTER_TYPE } from './const';
import { LIKE, EQ } from './comparison';
export const filterByText = _ => (data, dataField, { filterVal, comparator = LIKE }) =>
data.filter((row) => {
const cell = _.get(row, dataField);
const cellStr = _.isDefined(cell) ? cell.toString() : '';
if (comparator === EQ) {
return cellStr === filterVal;
}
return cellStr.indexOf(filterVal) > -1;
});
export const filterFactory = _ => (filterType) => {
let filterFn;
switch (filterType) {
case FILTER_TYPE.TEXT:
filterFn = filterByText(_);
break;
default:
filterFn = filterByText(_);
}
return filterFn;
};
export const filters = (store, _) => (currFilters) => {
const factory = filterFactory(_);
let result = store.getAllData();
let filterFn;
Object.keys(currFilters).forEach((dataField) => {
const filterObj = currFilters[dataField];
filterFn = factory(filterObj.filterType);
result = filterFn(result, dataField, filterObj);
});
return result;
};

View File

@ -0,0 +1,15 @@
import TextFilter from './components/text';
import FilterWrapper from './wrapper';
import * as Comparison from './comparison';
export default (options = {}) => ({
FilterWrapper,
options
});
export const Comparator = Comparison;
export const textFilter = (props = {}) => ({
Filter: TextFilter,
props
});

View File

@ -0,0 +1,48 @@
import { Component } from 'react';
import PropTypes from 'prop-types';
import { filters } from './filter';
export default class FilterWrapper extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
baseElement: PropTypes.func.isRequired,
_: PropTypes.object.isRequired
}
constructor(props) {
super(props);
this.state = { currFilters: {}, isDataChanged: false };
this.onFilter = this.onFilter.bind(this);
}
componentWillReceiveProps() {
this.setState(() => ({ isDataChanged: false }));
}
onFilter(column, filterVal, filterType) {
const { store, _ } = this.props;
const { currFilters } = this.state;
const { dataField, filter } = column;
if (!_.isDefined(filterVal) || filterVal === '') {
delete currFilters[dataField];
} else {
const { comparator } = filter.props;
currFilters[dataField] = { filterVal, filterType, comparator };
}
store.filteredData = filters(store, _)(currFilters);
store.filtering = Object.keys(currFilters).length > 0;
this.setState(() => ({ currFilters, isDataChanged: true }));
}
render() {
return this.props.baseElement({
...this.props,
key: 'table',
onFilter: this.onFilter,
isDataChanged: this.state.isDataChanged
});
}
}

View File

@ -6,11 +6,13 @@ import { getRowByRowId } from './rows';
export default class Store {
constructor(keyField) {
this._data = [];
this._filteredData = [];
this._keyField = keyField;
this._sortOrder = undefined;
this._sortField = undefined;
this._selected = [];
this._filtering = false;
this._isDataChanged = false;
}
edit(rowId, dataField, newValue) {
@ -24,12 +26,33 @@ export default class Store {
this.data = sort(this)(sortFunc);
}
get data() { return this._data; }
set data(data) { this._data = (data ? JSON.parse(JSON.stringify(data)) : []); }
getAllData() {
return this._data;
}
get data() {
if (this._filtering) {
return this._filteredData;
}
return this._data;
}
set data(data) {
if (this._filtering) {
this._filteredData = data;
} else {
this._data = (data ? JSON.parse(JSON.stringify(data)) : []);
}
}
get filteredData() { return this._filteredData; }
set filteredData(filteredData) { this._filteredData = filteredData; }
get keyField() { return this._keyField; }
set keyField(keyField) { this._keyField = keyField; }
get isDataChanged() { return this._isDataChanged; }
set isDataChanged(isDataChanged) { this._isDataChanged = isDataChanged; }
get sortOrder() { return this._sortOrder; }
set sortOrder(sortOrder) { this._sortOrder = sortOrder; }
@ -38,4 +61,7 @@ export default class Store {
get selected() { return this._selected; }
set selected(selected) { this._selected = selected; }
get filtering() { return this._filtering; }
set filtering(filtering) { this._filtering = filtering; }
}

View File

@ -22,9 +22,8 @@ export const pureTable = props =>
export const wrapWithFilter = (props) => {
if (props.filter) {
const { wrapper } = props.filter;
const FilterBase = wrapper(wrapWithSort, _);
return React.createElement(FilterBase, { ...props });
const { FilterWrapper } = props.filter;
return React.createElement(FilterWrapper, { ...props, baseElement: wrapWithSort, _ });
}
return wrapWithSort(props);
};