diff --git a/packages/react-bootstrap-table2-paginator/index.js b/packages/react-bootstrap-table2-paginator/index.js index 8750183..8b20636 100644 --- a/packages/react-bootstrap-table2-paginator/index.js +++ b/packages/react-bootstrap-table2-paginator/index.js @@ -1,6 +1,23 @@ -import createContext from './src/context'; +import React from 'react'; +import PropTypes from 'prop-types'; +import createBaseContext from './src/state-context'; +import createDataContext from './src/data-context'; export default (options = {}) => ({ - createContext, + createContext: createDataContext, options }); + +const { Provider, Consumer } = createBaseContext(); + +const CustomizableProvider = props => ( + + { paginationProps => props.children(paginationProps) } + +); + +CustomizableProvider.propTypes = { + children: PropTypes.func.isRequired +}; + +export const PaginationProvider = CustomizableProvider; diff --git a/packages/react-bootstrap-table2-paginator/src/context.js b/packages/react-bootstrap-table2-paginator/src/context.js deleted file mode 100644 index d5ffb96..0000000 --- a/packages/react-bootstrap-table2-paginator/src/context.js +++ /dev/null @@ -1,182 +0,0 @@ -/* eslint react/prop-types: 0 */ -/* eslint react/require-default-props: 0 */ -/* eslint no-lonely-if: 0 */ -import React from 'react'; -import PropTypes from 'prop-types'; - -import Const from './const'; -import { BootstrapContext } from './bootstrap'; -import Pagination from './pagination'; -import { getByCurrPage, alignPage } from './page'; - -export default ( - isRemotePagination, - handleRemotePageChange -) => { - const PaginationContext = React.createContext(); - - class PaginationProvider extends React.Component { - static propTypes = { - data: PropTypes.array.isRequired - } - - constructor(props) { - super(props); - this.handleChangePage = this.handleChangePage.bind(this); - this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this); - - let currPage; - let currSizePerPage; - const { options } = props.pagination; - const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST; - - // initialize current page - if (typeof options.page !== 'undefined') { - currPage = options.page; - } else if (typeof options.pageStartIndex !== 'undefined') { - currPage = options.pageStartIndex; - } else { - currPage = Const.PAGE_START_INDEX; - } - - // initialize current sizePerPage - if (typeof options.sizePerPage !== 'undefined') { - currSizePerPage = options.sizePerPage; - } else if (typeof sizePerPageList[0] === 'object') { - currSizePerPage = sizePerPageList[0].value; - } else { - currSizePerPage = sizePerPageList[0]; - } - - this.currPage = currPage; - this.currSizePerPage = currSizePerPage; - } - - componentWillReceiveProps(nextProps) { - let needNewState = false; - let { currPage } = this; - const { currSizePerPage } = this; - const { onPageChange } = nextProps.pagination.options; - - const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ? - nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX; - - // user should align the page when the page is not fit to the data size when remote enable - if (!isRemotePagination()) { - const newPage = alignPage(nextProps.data, currPage, currSizePerPage, pageStartIndex); - if (currPage !== newPage) { - currPage = newPage; - needNewState = true; - } - } else { - this.currPage = nextProps.pagination.options.page; - this.currSizePerPage = nextProps.pagination.options.sizePerPage; - } - - if (needNewState) { - if (onPageChange) { - onPageChange(currPage, currSizePerPage); - } - this.currPage = currPage; - this.currSizePerPage = currSizePerPage; - } - } - - handleChangePage(currPage) { - const { currSizePerPage } = this; - const { pagination: { options } } = this.props; - - if (options.onPageChange) { - options.onPageChange(currPage, currSizePerPage); - } - - this.currPage = currPage; - - if (isRemotePagination()) { - handleRemotePageChange(currPage, currSizePerPage); - return; - } - this.forceUpdate(); - } - - handleChangeSizePerPage(currSizePerPage, currPage) { - const { pagination: { options } } = this.props; - - if (options.onSizePerPageChange) { - options.onSizePerPageChange(currSizePerPage, currPage); - } - - this.currPage = currPage; - this.currSizePerPage = currSizePerPage; - - if (isRemotePagination()) { - handleRemotePageChange(currPage, currSizePerPage); - return; - } - this.forceUpdate(); - } - - render() { - let { data } = this.props; - const { pagination: { options }, bootstrap4 } = this.props; - const { currPage, currSizePerPage } = this; - const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ? - Const.With_FIRST_AND_LAST : options.withFirstAndLast; - const alwaysShowAllBtns = typeof options.alwaysShowAllBtns === 'undefined' ? - Const.SHOW_ALL_PAGE_BTNS : options.alwaysShowAllBtns; - const hideSizePerPage = typeof options.hideSizePerPage === 'undefined' ? - Const.HIDE_SIZE_PER_PAGE : options.hideSizePerPage; - const hidePageListOnlyOnePage = typeof options.hidePageListOnlyOnePage === 'undefined' ? - Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE : options.hidePageListOnlyOnePage; - const pageStartIndex = typeof options.pageStartIndex === 'undefined' ? - Const.PAGE_START_INDEX : options.pageStartIndex; - - data = isRemotePagination() ? - data : - getByCurrPage( - data, - currPage, - currSizePerPage, - pageStartIndex - ); - - return ( - - { this.props.children } - - - - - ); - } - } - - return { - Provider: PaginationProvider, - Consumer: PaginationContext.Consumer - }; -}; diff --git a/packages/react-bootstrap-table2-paginator/src/data-context.js b/packages/react-bootstrap-table2-paginator/src/data-context.js new file mode 100644 index 0000000..d9e9e01 --- /dev/null +++ b/packages/react-bootstrap-table2-paginator/src/data-context.js @@ -0,0 +1,100 @@ +/* eslint react/prop-types: 0 */ +/* eslint react/require-default-props: 0 */ +/* eslint no-lonely-if: 0 */ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Const from './const'; +import { BootstrapContext } from './bootstrap'; +import Pagination from './pagination'; +import { getByCurrPage, alignPage } from './page'; +import createBaseContext from './state-context'; + +const { Provider } = createBaseContext(); + +const PaginationDataContext = React.createContext(); + +class PaginationDataProvider extends Provider { + static propTypes = { + data: PropTypes.array.isRequired, + remoteEmitter: PropTypes.object.isRequired, + isRemotePagination: PropTypes.func.isRequired + } + + componentWillReceiveProps(nextProps) { + super.componentWillReceiveProps(nextProps); + const { currSizePerPage } = this; + const { custom, onPageChange } = nextProps.pagination.options; + + const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ? + nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX; + + // user should align the page when the page is not fit to the data size when remote enable + if (!this.isRemotePagination() && !custom) { + const newPage = alignPage( + nextProps.data.length, this.currPage, currSizePerPage, pageStartIndex); + + if (this.currPage !== newPage) { + if (onPageChange) { + onPageChange(newPage, currSizePerPage); + } + this.currPage = newPage; + } + } + } + + isRemotePagination = () => this.props.isRemotePagination(); + + renderDefaultPagination = () => { + if (!this.props.pagination.options.custom) { + const { + bootstrap4, + page: currPage, + sizePerPage: currSizePerPage, + dataSize, + ...rest + } = this.getPaginationProps(); + return ( + + + + ); + } + return null; + } + + render() { + let { data } = this.props; + const { pagination: { options } } = this.props; + const { currPage, currSizePerPage } = this; + const pageStartIndex = typeof options.pageStartIndex === 'undefined' ? + Const.PAGE_START_INDEX : options.pageStartIndex; + + data = this.isRemotePagination() ? + data : + getByCurrPage( + data, + currPage, + currSizePerPage, + pageStartIndex + ); + + return ( + + { this.props.children } + { this.renderDefaultPagination() } + + ); + } +} + +export default () => ({ + Provider: PaginationDataProvider, + Consumer: PaginationDataContext.Consumer +}); diff --git a/packages/react-bootstrap-table2-paginator/src/page.js b/packages/react-bootstrap-table2-paginator/src/page.js index feb1f0a..1e3e82b 100644 --- a/packages/react-bootstrap-table2-paginator/src/page.js +++ b/packages/react-bootstrap-table2-paginator/src/page.js @@ -18,13 +18,11 @@ const startIndex = ( ) => end - (sizePerPage - 1); export const alignPage = ( - data, + dataSize, page, sizePerPage, pageStartIndex ) => { - const dataSize = data.length; - if (page < pageStartIndex || page > (Math.floor(dataSize / sizePerPage) + pageStartIndex)) { return pageStartIndex; } diff --git a/packages/react-bootstrap-table2-paginator/src/state-context.js b/packages/react-bootstrap-table2-paginator/src/state-context.js new file mode 100644 index 0000000..122b62b --- /dev/null +++ b/packages/react-bootstrap-table2-paginator/src/state-context.js @@ -0,0 +1,163 @@ +/* eslint react/prop-types: 0 */ +/* eslint react/require-default-props: 0 */ +/* eslint no-lonely-if: 0 */ +import React from 'react'; +import Const from './const'; + +const StateContext = React.createContext(); + +class StateProvider extends React.Component { + constructor(props) { + super(props); + this.handleChangePage = this.handleChangePage.bind(this); + this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this); + + let currPage; + let currSizePerPage; + const { options } = props.pagination; + const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST; + + // initialize current page + if (typeof options.page !== 'undefined') { + currPage = options.page; + } else if (typeof options.pageStartIndex !== 'undefined') { + currPage = options.pageStartIndex; + } else { + currPage = Const.PAGE_START_INDEX; + } + + // initialize current sizePerPage + if (typeof options.sizePerPage !== 'undefined') { + currSizePerPage = options.sizePerPage; + } else if (typeof sizePerPageList[0] === 'object') { + currSizePerPage = sizePerPageList[0].value; + } else { + currSizePerPage = sizePerPageList[0]; + } + + this.currPage = currPage; + this.currSizePerPage = currSizePerPage; + } + + componentWillReceiveProps(nextProps) { + const { custom } = nextProps.pagination.options; + + // user should align the page when the page is not fit to the data size when remote enable + if (this.isRemotePagination() || custom) { + this.currPage = nextProps.pagination.options.page; + this.currSizePerPage = nextProps.pagination.options.sizePerPage; + } + } + + getPaginationProps = () => { + const { pagination: { options }, bootstrap4 } = this.props; + const { currPage, currSizePerPage } = this; + const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ? + Const.With_FIRST_AND_LAST : options.withFirstAndLast; + const alwaysShowAllBtns = typeof options.alwaysShowAllBtns === 'undefined' ? + Const.SHOW_ALL_PAGE_BTNS : options.alwaysShowAllBtns; + const hideSizePerPage = typeof options.hideSizePerPage === 'undefined' ? + Const.HIDE_SIZE_PER_PAGE : options.hideSizePerPage; + const hidePageListOnlyOnePage = typeof options.hidePageListOnlyOnePage === 'undefined' ? + Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE : options.hidePageListOnlyOnePage; + const pageStartIndex = typeof options.pageStartIndex === 'undefined' ? + Const.PAGE_START_INDEX : options.pageStartIndex; + return { + ...options, + bootstrap4, + page: currPage, + sizePerPage: currSizePerPage, + pageStartIndex, + hidePageListOnlyOnePage, + hideSizePerPage, + alwaysShowAllBtns, + withFirstAndLast, + dataSize: options.totalSize, + sizePerPageList: options.sizePerPageList || Const.SIZE_PER_PAGE_LIST, + paginationSize: options.paginationSize || Const.PAGINATION_SIZE, + showTotal: options.showTotal, + paginationTotalRenderer: options.paginationTotalRenderer, + firstPageText: options.firstPageText || Const.FIRST_PAGE_TEXT, + prePageText: options.prePageText || Const.PRE_PAGE_TEXT, + nextPageText: options.nextPageText || Const.NEXT_PAGE_TEXT, + lastPageText: options.lastPageText || Const.LAST_PAGE_TEXT, + prePageTitle: options.prePageTitle || Const.PRE_PAGE_TITLE, + nextPageTitle: options.nextPageTitle || Const.NEXT_PAGE_TITLE, + firstPageTitle: options.firstPageTitle || Const.FIRST_PAGE_TITLE, + lastPageTitle: options.lastPageTitle || Const.LAST_PAGE_TITLE, + onPageChange: this.handleChangePage, + onSizePerPageChange: this.handleChangeSizePerPage + }; + } + + setPaginationRemoteEmitter = (remoteEmitter) => { + this.remoteEmitter = remoteEmitter; + } + + isRemotePagination = () => { + const e = {}; + this.remoteEmitter.emit('isRemotePagination', e); + return e.result; + }; + + handleChangePage(currPage) { + const { currSizePerPage } = this; + const { pagination: { options } } = this.props; + + if (options.onPageChange) { + options.onPageChange(currPage, currSizePerPage); + } + + this.currPage = currPage; + + if (this.isRemotePagination()) { + this.remoteEmitter.emit('paginationChange', currPage, currSizePerPage); + return; + } + this.forceUpdate(); + } + + handleChangeSizePerPage(currSizePerPage, currPage) { + const { pagination: { options } } = this.props; + + if (options.onSizePerPageChange) { + options.onSizePerPageChange(currSizePerPage, currPage); + } + + this.currPage = currPage; + this.currSizePerPage = currSizePerPage; + + if (this.isRemotePagination()) { + this.remoteEmitter.emit('paginationChange', currPage, currSizePerPage); + return; + } + this.forceUpdate(); + } + + render() { + const paginationProps = this.getPaginationProps(); + const pagination = { + ...this.props.pagination, + options: paginationProps + }; + + return ( + + { this.props.children } + + ); + } +} + +export default () => ({ + Provider: StateProvider, + Consumer: StateContext.Consumer +}); diff --git a/packages/react-bootstrap-table2/src/contexts/index.js b/packages/react-bootstrap-table2/src/contexts/index.js index dc2f6a7..12bfb9a 100644 --- a/packages/react-bootstrap-table2/src/contexts/index.js +++ b/packages/react-bootstrap-table2/src/contexts/index.js @@ -40,8 +40,7 @@ const withContext = Base => } if (props.pagination) { - this.PaginationContext = props.pagination.createContext( - this.isRemotePagination, this.handleRemotePageChange); + this.PaginationContext = props.pagination.createContext(); } if (props.search && props.search.searchContext) { @@ -52,6 +51,10 @@ const withContext = Base => if (props.setDependencyModules) { props.setDependencyModules(_); } + + if (props.setPaginationRemoteEmitter) { + props.setPaginationRemoteEmitter(this.remoteEmitter); + } } componentWillReceiveProps(nextProps) { @@ -150,6 +153,8 @@ const withContext = Base => pagination={ this.props.pagination } data={ rootProps.getData(filterProps, searchProps, sortProps) } bootstrap4={ this.props.bootstrap4 } + isRemotePagination={ this.isRemotePagination } + remoteEmitter={ this.remoteEmitter } > { 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 89af4c6..b8f4935 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js +++ b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js @@ -1,7 +1,15 @@ +import EventEmitter from 'events'; import _ from '../utils'; export default ExtendBase => class RemoteResolver extends ExtendBase { + constructor(props) { + super(props); + this.remoteEmitter = new EventEmitter(); + this.remoteEmitter.on('paginationChange', this.handleRemotePageChange); + this.remoteEmitter.on('isRemotePagination', this.isRemotePagination); + } + getNewestState = (state = {}) => { let sortOrder; let sortField; @@ -47,9 +55,10 @@ export default ExtendBase => return remote === true || (_.isObject(remote) && remote.search) || this.isRemotePagination(); } - isRemotePagination = () => { + isRemotePagination = (e = {}) => { const { remote } = this.props; - return remote === true || (_.isObject(remote) && remote.pagination); + e.result = (remote === true || (_.isObject(remote) && remote.pagination)); + return e.result; } isRemoteFiltering = () => {