From 8f4dc9907a5772041e8c6bd5dbbffb9714c57b8f Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 20 May 2018 14:36:16 +0800 Subject: [PATCH] implement filter context --- .../react-bootstrap-table2-filter/index.js | 4 +- .../src/context.js | 94 +++++++++++++++ .../src/filter.js | 4 +- .../src/wrapper.js | 107 ------------------ .../src/contexts/index.js | 31 ++++- .../src/props-resolver/remote-resolver.js | 9 +- 6 files changed, 132 insertions(+), 117 deletions(-) create mode 100644 packages/react-bootstrap-table2-filter/src/context.js delete mode 100644 packages/react-bootstrap-table2-filter/src/wrapper.js diff --git a/packages/react-bootstrap-table2-filter/index.js b/packages/react-bootstrap-table2-filter/index.js index 6d9368a..af67dfc 100644 --- a/packages/react-bootstrap-table2-filter/index.js +++ b/packages/react-bootstrap-table2-filter/index.js @@ -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 }); diff --git a/packages/react-bootstrap-table2-filter/src/context.js b/packages/react-bootstrap-table2-filter/src/context.js new file mode 100644 index 0000000..1459b3f --- /dev/null +++ b/packages/react-bootstrap-table2-filter/src/context.js @@ -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 ( + + { this.props.children } + + ); + } + } + + return { + Provider: FilterProvider, + Consumer: FilterContext.Consumer + }; +}; diff --git a/packages/react-bootstrap-table2-filter/src/filter.js b/packages/react-bootstrap-table2-filter/src/filter.js index c1a8666..07454c4 100644 --- a/packages/react-bootstrap-table2-filter/src/filter.js +++ b/packages/react-bootstrap-table2-filter/src/filter.js @@ -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]; diff --git a/packages/react-bootstrap-table2-filter/src/wrapper.js b/packages/react-bootstrap-table2-filter/src/wrapper.js deleted file mode 100644 index 0602ee8..0000000 --- a/packages/react-bootstrap-table2-filter/src/wrapper.js +++ /dev/null @@ -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 ( - - ); - } - }; diff --git a/packages/react-bootstrap-table2/src/contexts/index.js b/packages/react-bootstrap-table2/src/contexts/index.js index 20ed702..f01d7d3 100644 --- a/packages/react-bootstrap-table2/src/contexts/index.js +++ b/packages/react-bootstrap-table2/src/contexts/index.js @@ -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) => ( this.sortContext = n } defaultSorted={ this.props.defaultSorted } defaultSortDirection={ this.props.defaultSortDirection } - data={ rootProps.data } + data={ filterProps ? filterProps.data : rootProps.data } > { @@ -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) => ( + this.filterContext = n } + data={ rootProps.data } + > + + { + filterProps => base(rootProps, cellEditprops, filterProps) + } + + + ); + } + renderWithCellEdit(base, baseProps) { return rootProps => ( { let base = this.renderBase(baseProps); + if (FilterContext) { + base = this.renderWithFilter(base, baseProps); + } + if (CellEditContext) { base = this.renderWithCellEdit(base, baseProps); } diff --git a/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js index 4d0e833..a6c94f6 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js +++ b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js @@ -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;