mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
implement filter context
This commit is contained in:
parent
2f7d0104a0
commit
8f4dc9907a
@ -3,12 +3,12 @@ import SelectFilter from './src/components/select';
|
||||
import MultiSelectFilter from './src/components/multiselect';
|
||||
import NumberFilter from './src/components/number';
|
||||
import DateFilter from './src/components/date';
|
||||
import wrapperFactory from './src/wrapper';
|
||||
import createContext from './src/context';
|
||||
import * as Comparison from './src/comparison';
|
||||
import { FILTER_TYPE } from './src/const';
|
||||
|
||||
export default (options = {}) => ({
|
||||
wrapperFactory,
|
||||
createContext,
|
||||
options
|
||||
});
|
||||
|
||||
|
||||
94
packages/react-bootstrap-table2-filter/src/context.js
vendored
Normal file
94
packages/react-bootstrap-table2-filter/src/context.js
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
/* eslint react/require-default-props: 0 */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { filters } from './filter';
|
||||
import { LIKE, EQ } from './comparison';
|
||||
import { FILTER_TYPE } from './const';
|
||||
|
||||
export default (
|
||||
_,
|
||||
isRemoteFiltering,
|
||||
handleFilterChange
|
||||
) => {
|
||||
const FilterContext = React.createContext();
|
||||
|
||||
class FilterProvider extends React.Component {
|
||||
static propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
columns: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.currFilters = {};
|
||||
this.onFilter = this.onFilter.bind(this);
|
||||
this.onExternalFilter = this.onExternalFilter.bind(this);
|
||||
}
|
||||
|
||||
onFilter(column, filterType) {
|
||||
return (filterVal) => {
|
||||
// watch out here if migration to context API, #334
|
||||
const currFilters = Object.assign({}, this.currFilters);
|
||||
const { dataField, filter } = column;
|
||||
|
||||
const needClearFilters =
|
||||
!_.isDefined(filterVal) ||
|
||||
filterVal === '' ||
|
||||
filterVal.length === 0;
|
||||
|
||||
if (needClearFilters) {
|
||||
delete currFilters[dataField];
|
||||
} else {
|
||||
// select default comparator is EQ, others are LIKE
|
||||
const {
|
||||
comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE),
|
||||
caseSensitive = false
|
||||
} = filter.props;
|
||||
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };
|
||||
}
|
||||
|
||||
this.currFilters = currFilters;
|
||||
|
||||
if (isRemoteFiltering()) {
|
||||
handleFilterChange(currFilters);
|
||||
// when remote filtering is enable, dont set currFilters state
|
||||
// in the componentWillReceiveProps,
|
||||
// it's the key point that we can know the filter is changed
|
||||
return;
|
||||
}
|
||||
|
||||
this.forceUpdate();
|
||||
};
|
||||
}
|
||||
|
||||
onExternalFilter(column, filterType) {
|
||||
return (value) => {
|
||||
this.onFilter(column, filterType)(value);
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
let { data } = this.props;
|
||||
if (!isRemoteFiltering()) {
|
||||
data = filters(data, this.props.columns, _)(this.currFilters);
|
||||
}
|
||||
return (
|
||||
<FilterContext.Provider value={ {
|
||||
data,
|
||||
onFilter: this.onFilter,
|
||||
onExternalFilter: this.onExternalFilter
|
||||
} }
|
||||
>
|
||||
{ this.props.children }
|
||||
</FilterContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Provider: FilterProvider,
|
||||
Consumer: FilterContext.Consumer
|
||||
};
|
||||
};
|
||||
@ -229,9 +229,9 @@ export const filterFactory = _ => (filterType) => {
|
||||
return filterFn;
|
||||
};
|
||||
|
||||
export const filters = (store, columns, _) => (currFilters) => {
|
||||
export const filters = (data, columns, _) => (currFilters) => {
|
||||
const factory = filterFactory(_);
|
||||
let result = store.getAllData();
|
||||
let result = data;
|
||||
let filterFn;
|
||||
Object.keys(currFilters).forEach((dataField) => {
|
||||
const filterObj = currFilters[dataField];
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
/* eslint no-param-reassign: 0 */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { filters } from './filter';
|
||||
import { LIKE, EQ } from './comparison';
|
||||
import { FILTER_TYPE } from './const';
|
||||
|
||||
export default (Base, {
|
||||
_,
|
||||
remoteResolver
|
||||
}) =>
|
||||
class FilterWrapper extends remoteResolver(Component) {
|
||||
static propTypes = {
|
||||
store: PropTypes.object.isRequired,
|
||||
columns: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { currFilters: {}, isDataChanged: props.isDataChanged || false };
|
||||
this.onFilter = this.onFilter.bind(this);
|
||||
this.onExternalFilter = this.onExternalFilter.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps({ isDataChanged, store, columns }) {
|
||||
// consider to use lodash.isEqual
|
||||
const isRemoteFilter = this.isRemoteFiltering() || this.isRemotePagination();
|
||||
if (isRemoteFilter ||
|
||||
JSON.stringify(this.state.currFilters) !== JSON.stringify(store.filters)) {
|
||||
// I think this condition only isRemoteFilter is enough
|
||||
store.filteredData = store.getAllData();
|
||||
this.setState(() => ({ isDataChanged: true, currFilters: store.filters }));
|
||||
} else {
|
||||
if (Object.keys(this.state.currFilters).length > 0) {
|
||||
store.filteredData = filters(store, columns, _)(this.state.currFilters);
|
||||
}
|
||||
this.setState(() => ({ isDataChanged }));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* filter the table like below:
|
||||
* onFilter(column, filterType)(filterVal)
|
||||
* @param {Object} column
|
||||
* @param {String} filterType
|
||||
* @param {String} filterVal - user input for filtering.
|
||||
*/
|
||||
onFilter(column, filterType) {
|
||||
return (filterVal) => {
|
||||
const { store, columns } = this.props;
|
||||
// watch out here if migration to context API, #334
|
||||
const currFilters = Object.assign({}, store.filters);
|
||||
const { dataField, filter } = column;
|
||||
|
||||
const needClearFilters =
|
||||
!_.isDefined(filterVal) ||
|
||||
filterVal === '' ||
|
||||
filterVal.length === 0;
|
||||
|
||||
if (needClearFilters) {
|
||||
delete currFilters[dataField];
|
||||
} else {
|
||||
// select default comparator is EQ, others are LIKE
|
||||
const {
|
||||
comparator = (
|
||||
(filterType === FILTER_TYPE.SELECT) || (
|
||||
filterType === FILTER_TYPE.MULTISELECT) ? EQ : LIKE
|
||||
),
|
||||
caseSensitive = false
|
||||
} = filter.props;
|
||||
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };
|
||||
}
|
||||
|
||||
store.filters = currFilters;
|
||||
|
||||
if (this.isRemoteFiltering() || this.isRemotePagination()) {
|
||||
this.handleRemoteFilterChange();
|
||||
// when remote filtering is enable, dont set currFilters state
|
||||
// in the componentWillReceiveProps,
|
||||
// it's the key point that we can know the filter is changed
|
||||
return;
|
||||
}
|
||||
|
||||
store.filteredData = filters(store, columns, _)(currFilters);
|
||||
this.setState(() => ({ currFilters, isDataChanged: true }));
|
||||
};
|
||||
}
|
||||
|
||||
onExternalFilter(column, filterType) {
|
||||
return (value) => {
|
||||
this.onFilter(column, filterType)(value);
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Base
|
||||
{ ...this.props }
|
||||
data={ this.props.store.data }
|
||||
onFilter={ this.onFilter }
|
||||
onExternalFilter={ this.onExternalFilter }
|
||||
isDataChanged={ this.state.isDataChanged }
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
/* eslint no-return-assign: 0 */
|
||||
/* eslint class-methods-use-this: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import _ from '../utils';
|
||||
import createDataContext from './data-context';
|
||||
@ -12,6 +13,7 @@ const withContext = (Base) => {
|
||||
let SelectionContext;
|
||||
let CellEditContext;
|
||||
let SortContext;
|
||||
let FilterContext;
|
||||
|
||||
return class BootstrapTableContainer extends remoteResolver(Component) {
|
||||
constructor(props) {
|
||||
@ -23,6 +25,10 @@ const withContext = (Base) => {
|
||||
CellEditContext = props.cellEdit.createContext(
|
||||
_, dataOperator, this.isRemoteCellEdit, this.handleCellChange);
|
||||
}
|
||||
if (props.filter) {
|
||||
FilterContext = props.filter.createContext(
|
||||
_, this.isRemoteFiltering, this.handleRemoteFilterChange);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@ -32,13 +38,13 @@ const withContext = (Base) => {
|
||||
}
|
||||
|
||||
renderBase(baseProps) {
|
||||
return (rootProps, cellEditProps) => (
|
||||
return (rootProps, cellEditProps, filterProps) => (
|
||||
<SortContext.Provider
|
||||
{ ...baseProps }
|
||||
ref={ n => this.sortContext = n }
|
||||
defaultSorted={ this.props.defaultSorted }
|
||||
defaultSortDirection={ this.props.defaultSortDirection }
|
||||
data={ rootProps.data }
|
||||
data={ filterProps ? filterProps.data : rootProps.data }
|
||||
>
|
||||
<SortContext.Consumer>
|
||||
{
|
||||
@ -56,6 +62,7 @@ const withContext = (Base) => {
|
||||
{ ...selectionProps }
|
||||
{ ...sortProps }
|
||||
{ ...cellEditProps }
|
||||
{ ...filterProps }
|
||||
data={ sortProps.data }
|
||||
/>
|
||||
)
|
||||
@ -69,6 +76,22 @@ const withContext = (Base) => {
|
||||
);
|
||||
}
|
||||
|
||||
renderWithFilter(base, baseProps) {
|
||||
return (rootProps, cellEditprops) => (
|
||||
<FilterContext.Provider
|
||||
{ ...baseProps }
|
||||
ref={ n => this.filterContext = n }
|
||||
data={ rootProps.data }
|
||||
>
|
||||
<FilterContext.Consumer>
|
||||
{
|
||||
filterProps => base(rootProps, cellEditprops, filterProps)
|
||||
}
|
||||
</FilterContext.Consumer>
|
||||
</FilterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
renderWithCellEdit(base, baseProps) {
|
||||
return rootProps => (
|
||||
<CellEditContext.Provider
|
||||
@ -91,6 +114,10 @@ const withContext = (Base) => {
|
||||
|
||||
let base = this.renderBase(baseProps);
|
||||
|
||||
if (FilterContext) {
|
||||
base = this.renderWithFilter(base, baseProps);
|
||||
}
|
||||
|
||||
if (CellEditContext) {
|
||||
base = this.renderWithCellEdit(base, baseProps);
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ export default ExtendBase =>
|
||||
sortField: this.sortContext.state.sortColumn ?
|
||||
this.sortContext.state.sortColumn.dataField :
|
||||
null,
|
||||
filters: this.filterContext ? this.filterContext.currFilters : {},
|
||||
...state,
|
||||
data: this.props.data
|
||||
};
|
||||
@ -29,9 +30,9 @@ export default ExtendBase =>
|
||||
return remote === true || (_.isObject(remote) && remote.pagination);
|
||||
}
|
||||
|
||||
isRemoteFiltering() {
|
||||
isRemoteFiltering = () => {
|
||||
const { remote } = this.props;
|
||||
return remote === true || (_.isObject(remote) && remote.filter);
|
||||
return remote === true || (_.isObject(remote) && remote.filter) || this.isRemotePagination();
|
||||
}
|
||||
|
||||
isRemoteSort = () => {
|
||||
@ -48,8 +49,8 @@ export default ExtendBase =>
|
||||
this.props.onTableChange('pagination', this.getNewestState());
|
||||
}
|
||||
|
||||
handleRemoteFilterChange() {
|
||||
const newState = {};
|
||||
handleRemoteFilterChange = (filters) => {
|
||||
const newState = { filters };
|
||||
if (this.isRemotePagination()) {
|
||||
const options = this.props.pagination.options || {};
|
||||
newState.page = _.isDefined(options.pageStartIndex) ? options.pageStartIndex : 1;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user