├── .vs
├── slnx.sqlite
├── VSWorkspaceState.json
└── pagination
│ └── v15
│ └── .suo
├── src
├── pagination-lib
│ ├── index.js
│ ├── utils.js
│ ├── createPaginator.js
│ ├── selectors.js
│ ├── paginationReducer.js
│ ├── VirtualizedListWrapper
│ │ └── VirtualList.js
│ └── paginationActions.js
├── index.js
├── index.css
├── Demo
│ ├── PageLayout
│ │ ├── TwoRowsLayout.js
│ │ ├── FixedMiddleWithFlexMargins.js
│ │ ├── NavigationLink.js
│ │ ├── Header.js
│ │ └── PageContentContainer.js
│ ├── StandardPagination
│ │ ├── ElementCard.js
│ │ ├── SearchForm.js
│ │ ├── StandardPaginationDemo.js
│ │ ├── Paginator.js
│ │ ├── PageItem.js
│ │ └── standardPaginationConfig.js
│ ├── VirtualListPaged
│ │ ├── VirtualListPagedDemo.js
│ │ ├── VirtualListWrapper.js
│ │ ├── SearchForm.js
│ │ ├── SimpleRowWithoutPage.js
│ │ └── paginationConfig.js
│ ├── StandardPaginationUpdateEntity
│ │ ├── SearchForm.js
│ │ ├── ElementCard.js
│ │ ├── PaginationWithUpdateDemo.js
│ │ ├── Paginator.js
│ │ ├── PageItem.js
│ │ ├── store
│ │ │ └── updateElement.js
│ │ └── paginationWithUpdateConfig.js
│ └── ExaminationsGenerator.js
├── App.css
├── AppMainContainer.js
├── store.js
├── App.js
├── logo.svg
└── registerServiceWorker.js
├── dist
├── index.js.map
├── index.js
├── utils.js
├── utils.js.map
├── createPaginator.js
├── selectors.js
├── createPaginator.js.map
├── selectors.js.map
├── paginationReducer.js
├── paginationReducer.js.map
├── paginationActions.js
├── VirtualizedListExtensions
│ ├── VirtualList.js.map
│ └── VirtualList.js
├── VirtualizedListWrapper
│ ├── VirtualList.js.map
│ └── VirtualList.js
└── paginationActions.js.map
├── public
├── manifest.json
└── index.html
├── .gitignore
├── LICENSE
├── package.json
└── Readme.md
/.vs/slnx.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tranzystor/redux-cached-pagination/HEAD/.vs/slnx.sqlite
--------------------------------------------------------------------------------
/.vs/VSWorkspaceState.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExpandedNodes": [
3 | ""
4 | ],
5 | "PreviewInSolutionExplorer": false
6 | }
--------------------------------------------------------------------------------
/.vs/pagination/v15/.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tranzystor/redux-cached-pagination/HEAD/.vs/pagination/v15/.suo
--------------------------------------------------------------------------------
/src/pagination-lib/index.js:
--------------------------------------------------------------------------------
1 | import { createPaginator } from './createPaginator';
2 | import VirtualList from './VirtualizedListWrapper/VirtualList';
3 |
4 | export { createPaginator, VirtualList };
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import registerServiceWorker from './registerServiceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 | registerServiceWorker();
9 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
7 | #react-paginate ul {
8 | display: inline-block;
9 | padding-left: 15px;
10 | padding-right: 15px;
11 | }
12 |
13 | #react-paginate li {
14 | display: inline-block;
15 | }
16 |
17 | #react-paginate .break a {
18 | cursor: default;
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/dist/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/pagination-lib/index.js"],"names":["createPaginator","VirtualList"],"mappings":";;;;;;;AAAA;;AACA;;;;;;QAESA,e;QAAiBC,W","file":"index.js","sourcesContent":["import { createPaginator } from './createPaginator';\r\nimport VirtualList from './VirtualizedListWrapper/VirtualList';\r\n\r\nexport { createPaginator, VirtualList };\r\n"]}
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/src/Demo/PageLayout/TwoRowsLayout.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const TwoRowsLayout = styled.div`
4 | display: flex;
5 | flex-direction: column;
6 | height: 100%;
7 | background: #f7f7f7;
8 |
9 | & > :nth-child(1) {
10 | height: 60px;
11 | flex: 0 0 auto;
12 | }
13 |
14 | & > :nth-child(2) {
15 | flex: 1 1 100%;
16 | }
17 | `;
18 |
19 | export default TwoRowsLayout;
20 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | @keyframes App-logo-spin {
22 | from { transform: rotate(0deg); }
23 | to { transform: rotate(360deg); }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Demo/StandardPagination/ElementCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 |
4 | const StyledCard = styled.div`
5 | border-radius: 2px;
6 | box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 1px;
7 | padding: 10px;
8 | `;
9 |
10 | class ElementCard extends Component {
11 | render() {
12 | return (
13 |
14 | id: {this.props.id} data: {this.props.examData}
15 |
16 | );
17 | }
18 | }
19 |
20 | export default ElementCard;
21 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.VirtualList = exports.createPaginator = undefined;
7 |
8 | var _createPaginator = require('./createPaginator');
9 |
10 | var _VirtualList = require('./VirtualizedListWrapper/VirtualList');
11 |
12 | var _VirtualList2 = _interopRequireDefault(_VirtualList);
13 |
14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15 |
16 | exports.createPaginator = _createPaginator.createPaginator;
17 | exports.VirtualList = _VirtualList2.default;
18 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/src/AppMainContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './App.css';
3 | import { connect } from 'react-redux';
4 | import { Link } from 'react-router-dom';
5 |
6 | class AppMainContainer extends Component {
7 | render() {
8 | return (
9 |
10 | - Standard List Demo
11 | - Virtual list
12 | - Standard list with element update
13 |
14 | );
15 | }
16 | }
17 |
18 | export default connect(null, null)(AppMainContainer)
19 |
--------------------------------------------------------------------------------
/src/Demo/VirtualListPaged/VirtualListPagedDemo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import SearchForm from './SearchForm';
3 | import VirtualListWrapper from './VirtualListWrapper';
4 | import styled from 'styled-components';
5 |
6 | const DemoContainer = styled.div`
7 | display: flex;
8 | flex-direction: column;
9 | width: 100%;
10 |
11 | & > *:nth-of-type(1) {
12 | margin-bottom: 10px;
13 | }
14 | `;
15 |
16 | class VirtualListPagedDemo extends Component {
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
23 | );
24 | }
25 | }
26 |
27 | export default VirtualListPagedDemo;
28 |
--------------------------------------------------------------------------------
/src/Demo/PageLayout/FixedMiddleWithFlexMargins.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 |
4 | //Trzy kolumny
5 | //środkowa ma ustaloną szerokość
6 | //lewa i prawa dopasowuje się do pozostałego wolnego miejsca
7 |
8 | const Wrapper = styled.div`
9 | display: flex;
10 | height: 100%;
11 |
12 | & > :nth-child(1) {
13 | flex: 1 1 auto;
14 | }
15 |
16 | & > :nth-child(2) {
17 | flex: 0 0 800px;
18 | }
19 |
20 | & > :nth-child(3) {
21 | flex: 1 1 auto;
22 | }
23 | `;
24 |
25 | class FixedMiddleWithFlexMargins extends Component {
26 | render() {
27 | return (
28 |
29 |
30 | {this.props.children}
31 |
32 |
33 | );
34 | }
35 | }
36 |
37 | export default FixedMiddleWithFlexMargins;
38 |
--------------------------------------------------------------------------------
/src/Demo/PageLayout/NavigationLink.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 | import { NavLink } from 'react-router-dom';
4 |
5 | const activeLinkClassName = 'activeLinkClassName';
6 | const StyledLink = styled(NavLink)`
7 | padding: 3px 0px;
8 | display: block;
9 |
10 | &:visited {
11 | color: #0a202d;
12 | }
13 |
14 | &:hover {
15 | color: black;
16 | }
17 |
18 | &:link {
19 | text-decoration: none;
20 | }
21 |
22 | &.${activeLinkClassName} > div {
23 | color: #2673a0;
24 | }
25 | `;
26 |
27 | class NavigationLink extends Component {
28 | render() {
29 | return (
30 |
31 | {this.props.children}
32 |
33 | );
34 | }
35 | }
36 |
37 | export default NavigationLink;
38 |
--------------------------------------------------------------------------------
/src/Demo/VirtualListPaged/VirtualListWrapper.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import VirtualList from './../../pagination-lib/VirtualizedListWrapper/VirtualList';
3 | import { examinationsPaginator } from './paginationConfig';
4 | import SimpleRowWithoutPage from './SimpleRowWithoutPage';
5 |
6 | const ROW_HEIGHT = 35;
7 |
8 | class VirtualListWrapper extends Component {
9 | constructor(props) {
10 | super(props);
11 | this.rowRenderer = this.rowRenderer.bind(this);
12 | }
13 |
14 | rowRenderer(index) {
15 | return (
16 |
17 | );
18 | }
19 |
20 | render() {
21 | return (
22 |
27 | );
28 | }
29 | }
30 | export default VirtualListWrapper;
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
2 | import reduxThunk from 'redux-thunk';
3 | import { examinationsPaginatorReducers, examinationsStoreName } from './Demo/VirtualListPaged/paginationConfig';
4 | import { examsPaginatorReducers, examsStoreName } from './Demo/StandardPagination/standardPaginationConfig';
5 | import { paginationUpdateReducers, paginationUpdateStoreName } from './Demo/StandardPaginationUpdateEntity/paginationWithUpdateConfig';
6 | import { globalExaminationsReducer } from './Demo/StandardPaginationUpdateEntity/store/updateElement';
7 |
8 | const rootReducer = combineReducers({
9 | [examinationsStoreName]: examinationsPaginatorReducers,
10 | [examsStoreName]: examsPaginatorReducers,
11 | [paginationUpdateStoreName]: paginationUpdateReducers,
12 | globalEntities: globalExaminationsReducer
13 | });
14 |
15 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
16 | export const store = createStore(
17 | rootReducer,
18 | composeEnhancers(
19 | applyMiddleware(reduxThunk)
20 | )
21 | );
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Tranzystor
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Demo/PageLayout/Header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 | import FixedMiddleWithFlexMargins from './FixedMiddleWithFlexMargins';
4 |
5 | const HeaderStyle = styled.div`
6 | height: 100%;
7 | background: #015f82;
8 | `;
9 |
10 | const CenterVertically = styled.div`
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | `;
15 |
16 | const HeaderLabel = styled.div`
17 | font-size: 24px;
18 | color: white;
19 | `;
20 |
21 | class Header extends Component {
22 | render() {
23 | return (
24 |
25 |
26 |
27 | DEMO redux-cached-pagination
28 |
36 |
37 |
38 |
39 | );
40 | }
41 | }
42 |
43 | export default Header;
44 |
--------------------------------------------------------------------------------
/src/Demo/VirtualListPaged/SearchForm.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import 'bootstrap/dist/css/bootstrap.css';
3 | import { connect } from 'react-redux';
4 | import { bindActionCreators } from 'redux';
5 | import { getSearchParams, updateSearchParams } from './paginationConfig';
6 | import AutosizeInput from 'react-input-autosize';
7 |
8 | const mapDispatchToProps = dispatch => {
9 | return bindActionCreators(
10 | {
11 | updateSearchParams
12 | },
13 | dispatch
14 | );
15 | };
16 |
17 | const mapStateToProps = (state, props) => {
18 | return {
19 | searchPhrase: getSearchParams(state).searchPhrase
20 | };
21 | };
22 |
23 | class SearchForm extends Component {
24 | inputChanged(event) {
25 | this.props.updateSearchParams({ searchPhrase: event.target.value });
26 | }
27 |
28 | render() {
29 | return (
30 |
37 | );
38 | }
39 | }
40 | export default connect(mapStateToProps, mapDispatchToProps)(SearchForm);
41 |
--------------------------------------------------------------------------------
/src/Demo/PageLayout/PageContentContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 | import { Route } from 'react-router-dom';
4 |
5 | import AppMainContainer from '../../AppMainContainer';
6 | import StandardPaginationDemo from '../StandardPagination/StandardPaginationDemo';
7 | import PaginationWithUpdateDemo from '../StandardPaginationUpdateEntity/PaginationWithUpdateDemo';
8 | import VirtualListPagedDemo from '../VirtualListPaged/VirtualListPagedDemo';
9 |
10 | const ContentStyle = styled.div`
11 | display: flex;
12 | width: 640px;
13 | background: white;
14 | overflow: auto;
15 |
16 | margin-bottom: 20px;
17 | padding: 40px;
18 | padding-top: 10px;
19 | border-radius: 2px;
20 | box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 1px;
21 | `;
22 |
23 | class PageContentContainer extends Component {
24 | render() {
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 | }
35 |
36 | export default PageContentContainer;
37 |
--------------------------------------------------------------------------------
/src/Demo/StandardPagination/SearchForm.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import 'bootstrap/dist/css/bootstrap.css';
3 | import { connect } from 'react-redux';
4 | import { bindActionCreators } from 'redux';
5 | import { getSearchParams, updateSearchParams } from './standardPaginationConfig';
6 | import AutosizeInput from 'react-input-autosize';
7 |
8 | const mapDispatchToProps = dispatch => {
9 | return bindActionCreators(
10 | {
11 | updateSearchParams
12 | },
13 | dispatch
14 | );
15 | };
16 |
17 | const mapStateToProps = (state, props) => {
18 | return {
19 | searchPhrase: getSearchParams(state).searchPhrase
20 | };
21 | };
22 |
23 | class SearchForm extends Component {
24 | inputChanged(event) {
25 | this.props.onSearchPhraseChanged(event.target.value);
26 | this.props.updateSearchParams({ searchPhrase: event.target.value });
27 | }
28 |
29 | render() {
30 | return (
31 |
38 | );
39 | }
40 | }
41 | export default connect(mapStateToProps, mapDispatchToProps)(SearchForm);
42 |
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/SearchForm.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import 'bootstrap/dist/css/bootstrap.css';
3 | import { connect } from 'react-redux';
4 | import { bindActionCreators } from 'redux';
5 | import { getSearchParams, updateSearchParams } from './paginationWithUpdateConfig';
6 | import AutosizeInput from 'react-input-autosize';
7 |
8 | const mapDispatchToProps = dispatch => {
9 | return bindActionCreators(
10 | {
11 | updateSearchParams
12 | },
13 | dispatch
14 | );
15 | };
16 |
17 | const mapStateToProps = (state, props) => {
18 | return {
19 | searchPhrase: getSearchParams(state).searchPhrase
20 | };
21 | };
22 |
23 | class SearchForm extends Component {
24 | inputChanged(event) {
25 | this.props.onSearchPhraseChanged(event.target.value);
26 | this.props.updateSearchParams({ searchPhrase: event.target.value });
27 | }
28 |
29 | render() {
30 | return (
31 |
38 | );
39 | }
40 | }
41 | export default connect(mapStateToProps, mapDispatchToProps)(SearchForm);
42 |
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/ElementCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { bindActionCreators } from 'redux';
4 | import { updateEntities } from './store/updateElement';
5 | import styled from 'styled-components';
6 |
7 | const StyledCard = styled.div`
8 | border-radius: 2px;
9 | box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 1px;
10 | padding: 10px;
11 | `;
12 |
13 | const mapDispatchToProps = dispatch => {
14 | return bindActionCreators(
15 | {
16 | updateEntities
17 | },
18 | dispatch
19 | );
20 | };
21 |
22 | class ElementCard extends Component {
23 | constructor(props) {
24 | super(props);
25 | this.onUpdate = this.onUpdate.bind(this);
26 | }
27 |
28 | onUpdate() {
29 | console.log('update: ' + this.props.id);
30 | this.props.updateEntities(this.props.id, 'abc');
31 | }
32 |
33 | render() {
34 | return (
35 |
36 | ElementCard id: {this.props.id} examData: {this.props.examData}
37 | {this.props.isUpdating && ' isUpdating'}
38 | {this.props.isUpdatingSuccess && ' success'}
39 | {this.props.isUpdatingFailure && ' failed'}
40 |
41 | );
42 | }
43 | }
44 |
45 | export default connect(null, mapDispatchToProps)(ElementCard);
46 |
--------------------------------------------------------------------------------
/src/pagination-lib/utils.js:
--------------------------------------------------------------------------------
1 | export const buildFetchingApiActionNames = actionPrefix => {
2 | return {
3 | fetching: `${actionPrefix}_FETCHING`,
4 | success: `${actionPrefix}_SUCCESS`,
5 | failure: `${actionPrefix}_FAILURE`,
6 | removeCache: `${actionPrefix}_SOURCE_CHANGED`,
7 | removeSearchResult: `${actionPrefix}_REMOVE_SEARCH_RESULT`,
8 | refreshing: `${actionPrefix}_REFRESHING_PAGE`,
9 | updatePaginationParams: `${actionPrefix}_UPDATE_PAGINATION_PARAMS`,
10 | updateScrollToIndex: `${actionPrefix}_UPDATE_SCROLL_TO_INDEX`,//obsolete
11 | updateSearchParams: `${actionPrefix}_UPDATE_SEARCH_PARAMS`,
12 | setCurrentPageNumber: `${actionPrefix}_SET_CURRENT_PAGE`
13 | }
14 | }
15 |
16 | export const buildStoreName = storePrefix => {
17 | return `${storePrefix}_store`;
18 | }
19 |
20 | export const buildUniqueKey = (obj) => {
21 | if (!obj) {
22 | console.error('input has to be defined!');
23 | }
24 |
25 | let result = '';
26 | Object.entries(obj).sort().forEach(
27 | ([key, value]) => {
28 | result = result.concat(key, '_', value, '_');
29 | }
30 | );
31 | return result;
32 | }
33 |
34 | export const convertTotalItemIndexToPageNum = (elementsPerPage) => (totalIndex) => {
35 | return Math.floor(totalIndex / elementsPerPage) + 1;
36 | }
37 |
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/PaginationWithUpdateDemo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 | import Paginator from './Paginator';
4 | import PageItem from './PageItem';
5 | import SearchForm from './SearchForm';
6 | import { bindActionCreators } from 'redux';
7 | import { connect } from 'react-redux';
8 | import { setCurrentPage } from './paginationWithUpdateConfig';
9 |
10 | const DemoContainer = styled.div`
11 | width: 100%;
12 | height: 100%;
13 | display: flex;
14 | flex-direction: column;
15 |
16 | & > *:nth-child(1) {
17 | align-self: center;
18 | }
19 |
20 | & > *:nth-child(2) {
21 | flex: none;
22 | margin-bottom: 10px;
23 | }
24 | `;
25 |
26 | const mapDispatchToProps = dispatch => {
27 | return bindActionCreators(
28 | {
29 | setCurrentPage
30 | },
31 | dispatch
32 | );
33 | };
34 |
35 | class PaginationWithUpdateDemo extends Component {
36 | onSearchPhraseChanged(searchPhrase) {
37 | this.props.setCurrentPage(1);
38 | }
39 |
40 | render() {
41 | return (
42 |
43 |
44 |
45 |
46 |
47 | );
48 | }
49 | }
50 |
51 | export default connect(null, mapDispatchToProps)(PaginationWithUpdateDemo);
52 |
--------------------------------------------------------------------------------
/src/Demo/StandardPagination/StandardPaginationDemo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 | import Paginator from './Paginator';
4 | import PageItem from './PageItem';
5 | import SearchForm from './SearchForm';
6 | import { bindActionCreators } from 'redux';
7 | import { connect } from 'react-redux';
8 | import { setCurrentPage } from './standardPaginationConfig';
9 |
10 | const DemoContainer = styled.div`
11 | width: 100%;
12 | height: 100%;
13 | display: flex;
14 | flex-direction: column;
15 |
16 | & > *:nth-child(1) {
17 | align-self: center;
18 | }
19 |
20 | & > *:nth-child(2) {
21 | flex: none;
22 | margin-bottom: 10px;
23 | }
24 | `;
25 |
26 | const mapDispatchToProps = dispatch => {
27 | return bindActionCreators(
28 | {
29 | setCurrentPage
30 | },
31 | dispatch
32 | );
33 | };
34 |
35 | const mapStateToProps = (state, props) => {
36 | return {};
37 | };
38 |
39 | class StandardPaginationDemo extends Component {
40 | onSearchPhraseChanged(searchPhrase) {
41 | this.props.setCurrentPage(1);
42 | }
43 |
44 | render() {
45 | return (
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 | }
54 |
55 | export default connect(mapStateToProps, mapDispatchToProps)(StandardPaginationDemo);
56 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 |
36 |
37 |
38 |
39 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/Demo/StandardPagination/Paginator.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Pagination from 'react-js-pagination';
3 | import 'bootstrap/dist/css/bootstrap.css';
4 | import { connect } from 'react-redux';
5 | import { getTotalElements, ELEMENTS_PER_PAGE, setCurrentPage, getCurrentPage } from './standardPaginationConfig';
6 | import { bindActionCreators } from 'redux';
7 |
8 | const mapStateToProps = (state, props) => {
9 | const totalItemsCount = getTotalElements(state);
10 | const activePage = getCurrentPage(state).pageNumber;
11 | return {
12 | totalItemsCount,
13 | activePage,
14 | }
15 | }
16 |
17 | const mapDispatchToProps = (dispatch) => {
18 | return bindActionCreators({
19 | setCurrentPage
20 | }, dispatch);
21 | }
22 |
23 | class Paginator extends Component {
24 |
25 | constructor(props) {
26 | super(props);
27 | this.onChosenPageChanged = this.onChosenPageChanged.bind(this);
28 | }
29 |
30 | onChosenPageChanged(page) {
31 | this.props.setCurrentPage(page);
32 | }
33 |
34 | render() {
35 | const { totalItemsCount } = this.props;
36 | return (
37 |
44 | );
45 | }
46 | }
47 | export default connect(mapStateToProps, mapDispatchToProps)(Paginator)
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/Paginator.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Pagination from 'react-js-pagination';
3 | import 'bootstrap/dist/css/bootstrap.css';
4 | import { connect } from 'react-redux';
5 | import { getTotalElements, ELEMENTS_PER_PAGE, setCurrentPage, getCurrentPage } from './paginationWithUpdateConfig';
6 | import { bindActionCreators } from 'redux';
7 |
8 | const mapStateToProps = (state, props) => {
9 | const totalItemsCount = getTotalElements(state);
10 | const activePage = getCurrentPage(state).pageNumber;
11 | return {
12 | totalItemsCount,
13 | activePage,
14 | }
15 | }
16 |
17 | const mapDispatchToProps = (dispatch) => {
18 | return bindActionCreators({
19 | setCurrentPage
20 | }, dispatch);
21 | }
22 |
23 | class Paginator extends Component {
24 |
25 | constructor(props) {
26 | super(props);
27 | this.onChosenPageChanged = this.onChosenPageChanged.bind(this);
28 | }
29 |
30 | onChosenPageChanged(page) {
31 | this.props.setCurrentPage(page);
32 | }
33 |
34 |
35 | render() {
36 | const { totalItemsCount } = this.props;
37 | return (
38 |
45 | );
46 | }
47 | }
48 | export default connect(mapStateToProps, mapDispatchToProps)(Paginator)
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './App.css';
3 | import { Provider } from 'react-redux';
4 | import { store } from './store';
5 | import { HashRouter as Router } from 'react-router-dom';
6 | import styled from 'styled-components';
7 | import TwoRowsLayout from './Demo/PageLayout/TwoRowsLayout';
8 | import Header from './Demo/PageLayout/Header';
9 | import FixedMiddleWithFlexMargins from './Demo/PageLayout/FixedMiddleWithFlexMargins';
10 | import PageContentContainer from './Demo/PageLayout/PageContentContainer';
11 | import NavigationLink from './Demo/PageLayout/NavigationLink';
12 |
13 | const TwoColumns = styled.div`
14 | display: flex;
15 | margin-top: 40px;
16 | `;
17 |
18 | const MenuContainer = styled.div`
19 | width: 160px;
20 | `;
21 |
22 | class App extends Component {
23 | render() {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Standard
33 | With entity update
34 | Virtual
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 | }
45 |
46 | export default App;
47 |
--------------------------------------------------------------------------------
/src/Demo/VirtualListPaged/SimpleRowWithoutPage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 | import { connect } from 'react-redux';
4 | import {
5 | getPaginationParams,
6 | getElementByTotalIndex,
7 | } from './paginationConfig';
8 |
9 | const SimpleRowStyled = styled.div`
10 | height: 30px;
11 | background: ${props => (props.isSelected ? 'rgb(238,243,242)' : 'white')};
12 | &:hover {
13 | background: rgb(221, 231, 229);
14 | }
15 | `;
16 |
17 | const mapStateToProps = (state, props) => {
18 | const rowContent = getElementByTotalIndex(state, props.index);
19 | return {
20 | selectedIndex: getPaginationParams(state).selectedIndex,
21 | rowContent
22 | };
23 | };
24 |
25 | class SimpleRowWithoutPage extends Component {
26 |
27 | render() {
28 | if (!this.props.rowContent) {
29 | return no data available
;
30 | }
31 |
32 | const content = this.props.rowContent;
33 | if (content.isFetching) {
34 | return isFetching ...
;
35 | }
36 | if (content.isFailed) {
37 | return isFailed
;
38 | }
39 | if (content.isSuccess || content.isRefreshing) {
40 | if (!content.id) {
41 | return no elements found
;
42 | }
43 |
44 | const isSelected = this.props.selectedIndex === this.props.index;
45 | return (
46 |
47 | SimpleRow: {content.id} dataa: {content.examData} {content.isRefreshing && ' refreshing'}
48 |
49 | );
50 | }
51 | }
52 | }
53 |
54 | export default connect(mapStateToProps, null)(SimpleRowWithoutPage);
55 |
--------------------------------------------------------------------------------
/src/Demo/ExaminationsGenerator.js:
--------------------------------------------------------------------------------
1 | class ExaminationsSource {
2 | constructor(name) {
3 | this.generateExaminations();
4 | }
5 |
6 | generateExaminations() {
7 | const initialCount = 800;
8 | this._examinations = [];
9 | for (let i = 0; i < initialCount; i++) {
10 | this._examinations.push(this.createExamination(i));
11 | }
12 | }
13 |
14 | createExamination(index) {
15 | return {
16 | examData: this.generateId(6),
17 | examIndexAtAll: index,
18 | id: index + 100000
19 | };
20 | }
21 |
22 | getExaminationsPage(pageNum, pageLength, phrase) {
23 | const pageFromZero = pageNum - 1;
24 | let elementsWithPhrase = this._examinations;
25 | if (phrase !== undefined && phrase !== null) {
26 | elementsWithPhrase = this._examinations.filter(e => e.examData.toLowerCase().includes(phrase.toLowerCase()));
27 | }
28 |
29 | const elementsWithPhraseCount = elementsWithPhrase.length;
30 | const pageElements = elementsWithPhrase.slice(pageFromZero * pageLength, pageFromZero * pageLength + pageLength);
31 |
32 | return {
33 | totalElements: elementsWithPhraseCount,
34 | elements: pageElements
35 | };
36 | }
37 |
38 | updateEntity(entityId, data) {
39 | let toUpdate = this._examinations.find(e => e.id === entityId);
40 | toUpdate.examData = data;
41 | return toUpdate;
42 | }
43 |
44 | generateId(length) {
45 | var text = '';
46 | var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
47 |
48 | for (var i = 0; i < length; i++) {
49 | text += possible.charAt(Math.floor(Math.random() * possible.length));
50 | }
51 |
52 | return text;
53 | }
54 | }
55 |
56 | const provider = new ExaminationsSource();
57 | export { provider };
58 |
--------------------------------------------------------------------------------
/src/Demo/StandardPagination/PageItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { bindActionCreators } from 'redux';
4 | import { getPage, loadExaminationsPage, getCurrentPage } from './standardPaginationConfig';
5 | import ElementCard from './ElementCard';
6 |
7 | const mapDispatchToProps = dispatch => {
8 | return bindActionCreators(
9 | {
10 | loadExaminationsPage
11 | },
12 | dispatch
13 | );
14 | };
15 |
16 | const mapStateToProps = (state, props) => {
17 | const chosenPage = getCurrentPage(state).pageNumber;
18 | const page = getPage(state, chosenPage);
19 | return {
20 | chosenPage,
21 | page
22 | };
23 | };
24 |
25 | class PageItem extends Component {
26 | componentWillMount() {
27 | this.props.loadExaminationsPage(1);
28 | }
29 |
30 | componentWillReceiveProps(nextProps) {
31 | this.props.loadExaminationsPage(nextProps.chosenPage);
32 | }
33 |
34 | render() {
35 | const page = this.props.page;
36 | if (!page) {
37 | return no data
;
38 | }
39 | if (page.isFetching) {
40 | return Loading ...
;
41 | }
42 |
43 | if (page.isSuccess || page.isRefreshing) {
44 | if (page.elements.length === 0) {
45 | return elements not found
;
46 | }
47 |
48 | const content = page.elements.map((x, i) => {
49 | return ;
50 | });
51 | return (
52 |
53 | {page.isRefreshing &&
refreshing ...
}
54 | {content}
55 |
56 | );
57 | }
58 |
59 | if (page.isFailed) {
60 | return isFailed
;
61 | }
62 | }
63 | }
64 |
65 | export default connect(mapStateToProps, mapDispatchToProps)(PageItem);
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-cached-pagination",
3 | "version": "0.2.0",
4 | "main": "./dist/index.js",
5 | "homepage": "https://tranzystor.github.io/redux-cached-pagination",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/Tranzystor/redux-cached-pagination"
9 | },
10 | "files": ["dist"],
11 | "dependencies": {
12 | "dot-prop-immutable": "^1.4.0",
13 | "react-virtualized": "^9.15.0",
14 | "styled-components": "^2.4.0"
15 | },
16 | "keywords": [
17 | "pagination",
18 | "react",
19 | "reactjs",
20 | "list",
21 | "page",
22 | "virtualized",
23 | "refresh",
24 | "redux",
25 | "normalizr",
26 | "cache",
27 | "cached",
28 | "virtual",
29 | "scrolling"
30 | ],
31 | "scripts": {
32 | "predeploy": "npm run build",
33 | "deploy": "gh-pages -d build",
34 | "prepublish":
35 | "babel ./src/pagination-lib --out-dir ./dist --source-maps --presets es2015,stage-0,react",
36 | "start": "react-scripts start",
37 | "build": "react-scripts build",
38 | "test": "react-scripts test --env=jsdom",
39 | "eject": "react-scripts eject"
40 | },
41 | "devDependencies": {
42 | "gh-pages": "^1.1.0",
43 | "babel-cli": "^6.18.0",
44 | "babel-core": "^6.18.2",
45 | "babel-jest": "^17.0.2",
46 | "babel-loader": "^6.2.8",
47 | "babel-preset-es2015": "^6.18.0",
48 | "babel-preset-react": "^6.16.0",
49 | "babel-preset-stage-0": "^6.16.0",
50 | "bootstrap": "^3.3.7",
51 | "normalizr": "^3.2.3",
52 | "react-input-autosize": "^2.1.0",
53 | "react-js-pagination": "^2.2.0",
54 | "react-scripts": "1.0.10",
55 | "redux-devtools": "^3.4.0",
56 | "react": "^16.2.0",
57 | "redux": "^3.7.2",
58 | "redux-thunk": "^2.2.0",
59 | "react-router-dom": "^4.2.2",
60 | "react-redux": "^5.0.5",
61 | "react-dom": "^16.2.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/PageItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { bindActionCreators } from 'redux';
4 | import { loadExaminationsPage, getCurrentPage } from './paginationWithUpdateConfig';
5 | import { getPageWithGlobalElements } from './store/updateElement';
6 | import ElementCard from './ElementCard';
7 |
8 | const mapDispatchToProps = dispatch => {
9 | return bindActionCreators(
10 | {
11 | loadExaminationsPage
12 | },
13 | dispatch
14 | );
15 | };
16 |
17 | const mapStateToProps = (state, props) => {
18 | const chosenPage = getCurrentPage(state).pageNumber;
19 | const pageGlobal = getPageWithGlobalElements(state, chosenPage);
20 | return {
21 | chosenPage,
22 | page: pageGlobal
23 | };
24 | };
25 |
26 | class PageItem extends Component {
27 | componentWillMount() {
28 | this.props.loadExaminationsPage(1);
29 | }
30 |
31 | componentWillReceiveProps(nextProps) {
32 | this.props.loadExaminationsPage(nextProps.chosenPage);
33 | }
34 |
35 | render() {
36 | const page = this.props.page;
37 | if (!page) {
38 | return no data
;
39 | }
40 | if (page.isFetching) {
41 | return isFetching ..
;
42 | }
43 |
44 | if (page.isSuccess || page.isRefreshing) {
45 | if (page.elements.length === 0) {
46 | return elements not found
;
47 | }
48 |
49 | const content = page.elements.map((x, i) => {
50 | return ;
51 | });
52 | return (
53 |
54 | {page.isRefreshing &&
refreshing
}
55 | {content}
56 |
57 | );
58 | }
59 |
60 | if (page.isFailed) {
61 | return isFailed
;
62 | }
63 | }
64 | }
65 |
66 | export default connect(mapStateToProps, mapDispatchToProps)(PageItem);
67 |
--------------------------------------------------------------------------------
/src/Demo/VirtualListPaged/paginationConfig.js:
--------------------------------------------------------------------------------
1 | import { createPaginator } from './../../pagination-lib/createPaginator';
2 | import { normalize, schema } from 'normalizr';
3 | import { provider } from '../ExaminationsGenerator';
4 |
5 | export const ELEMENTS_PER_PAGE = 30;
6 |
7 | const callExaminationsApi = (page, requestParams) => {
8 | const { searchPhrase } = requestParams;
9 | return new Promise((resolve, reject) => {
10 | setTimeout(() => {
11 | const serverResponse = provider.getExaminationsPage(page, ELEMENTS_PER_PAGE, searchPhrase);
12 | const formattedResponse = normalizeResponse(serverResponse);
13 | resolve(formattedResponse);
14 | }, 2000);
15 | });
16 | };
17 |
18 | const examination = new schema.Entity('examinations');
19 | const responseSchema = new schema.Object({ elements: new schema.Array(examination) });
20 |
21 | const normalizeResponse = serverResponse => {
22 | const normalizedData = normalize(serverResponse, responseSchema);
23 | const notNullEntities = normalizedData.entities.examinations ? normalizedData.entities.examinations : [];
24 | return {
25 | totalElements: serverResponse.totalElements,
26 | elements: normalizedData.result.elements,
27 | entities: notNullEntities
28 | };
29 | };
30 |
31 | const searchParamsInitialState = { searchPhrase: '' };
32 |
33 | const config = {
34 | refreshResultInBackground: true,
35 | timeToRefresh: 7000, //[ms]
36 | searchHistoryLength: 5,
37 | elementsPerPage: ELEMENTS_PER_PAGE
38 | };
39 |
40 | export const examinationsPaginator = createPaginator(
41 | 'virtual',
42 | callExaminationsApi,
43 | config,
44 | searchParamsInitialState
45 | );
46 |
47 | //redux
48 | export const examinationsStoreName = examinationsPaginator.storeName;
49 | export const examinationsPaginatorReducers = examinationsPaginator.reducers;
50 |
51 | export const updateSearchParams = examinationsPaginator.updateSearchParams;
52 |
53 | //selectors:
54 | export const getPaginationParams = examinationsPaginator.selectors.getPaginationParams;
55 | export const getSearchParams = examinationsPaginator.selectors.getSearchParams;
56 | export const getElementByTotalIndex = examinationsPaginator.selectors.getElementByTotalIndex;
57 |
--------------------------------------------------------------------------------
/dist/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
8 |
9 | var buildFetchingApiActionNames = exports.buildFetchingApiActionNames = function buildFetchingApiActionNames(actionPrefix) {
10 | return {
11 | fetching: actionPrefix + '_FETCHING',
12 | success: actionPrefix + '_SUCCESS',
13 | failure: actionPrefix + '_FAILURE',
14 | removeCache: actionPrefix + '_SOURCE_CHANGED',
15 | removeSearchResult: actionPrefix + '_REMOVE_SEARCH_RESULT',
16 | refreshing: actionPrefix + '_REFRESHING_PAGE',
17 | updatePaginationParams: actionPrefix + '_UPDATE_PAGINATION_PARAMS',
18 | updateScrollToIndex: actionPrefix + '_UPDATE_SCROLL_TO_INDEX', //obsolete
19 | updateSearchParams: actionPrefix + '_UPDATE_SEARCH_PARAMS',
20 | setCurrentPageNumber: actionPrefix + '_SET_CURRENT_PAGE'
21 | };
22 | };
23 |
24 | var buildStoreName = exports.buildStoreName = function buildStoreName(storePrefix) {
25 | return storePrefix + '_store';
26 | };
27 |
28 | var buildUniqueKey = exports.buildUniqueKey = function buildUniqueKey(obj) {
29 | if (!obj) {
30 | console.error('input has to be defined!');
31 | }
32 |
33 | var result = '';
34 | Object.entries(obj).sort().forEach(function (_ref) {
35 | var _ref2 = _slicedToArray(_ref, 2),
36 | key = _ref2[0],
37 | value = _ref2[1];
38 |
39 | result = result.concat(key, '_', value, '_');
40 | });
41 | return result;
42 | };
43 |
44 | var convertTotalItemIndexToPageNum = exports.convertTotalItemIndexToPageNum = function convertTotalItemIndexToPageNum(elementsPerPage) {
45 | return function (totalIndex) {
46 | return Math.floor(totalIndex / elementsPerPage) + 1;
47 | };
48 | };
49 | //# sourceMappingURL=utils.js.map
--------------------------------------------------------------------------------
/src/Demo/StandardPagination/standardPaginationConfig.js:
--------------------------------------------------------------------------------
1 | import { createPaginator } from './../../pagination-lib/createPaginator';
2 | import { normalize, schema } from 'normalizr';
3 | import { provider } from '../ExaminationsGenerator';
4 |
5 | export const ELEMENTS_PER_PAGE = 15;
6 |
7 | const callExaminationsApi = (page, requestParams) => {
8 | const { searchPhrase } = requestParams;
9 | return new Promise((resolve, reject) => {
10 | setTimeout(() => {
11 | const serverResponse = provider.getExaminationsPage(page, ELEMENTS_PER_PAGE, searchPhrase);
12 | const formattedResponse = normalizeResponse(serverResponse);
13 | resolve(formattedResponse);
14 | }, 1500);
15 | });
16 | };
17 |
18 | const examination = new schema.Entity('examinations');
19 | const responseSchema = new schema.Object({ elements: new schema.Array(examination) });
20 |
21 | const normalizeResponse = serverResponse => {
22 | const normalizedData = normalize(serverResponse, responseSchema);
23 | const notNullEntities = normalizedData.entities.examinations ? normalizedData.entities.examinations : [];
24 | return {
25 | totalElements: serverResponse.totalElements,
26 | elements: normalizedData.result.elements,
27 | entities: notNullEntities
28 | };
29 | };
30 |
31 | const searchParamsInitState = { searchPhrase: '' };
32 |
33 | //default config below
34 | const config = {
35 | refreshResultInBackground: true,
36 | timeToRefresh: 1000, //[ms]
37 | searchHistoryLength: 5,
38 | elementsPerPage: ELEMENTS_PER_PAGE
39 | };
40 |
41 | const examinationsPaginator = createPaginator(
42 | 'standard',
43 | callExaminationsApi,
44 | config,
45 | searchParamsInitState
46 | );
47 |
48 | export const paginatorStoreName = examinationsPaginator.storeName;
49 |
50 | //export used methods
51 | export const loadExaminationsPage = examinationsPaginator.requestPage;
52 | export const examsPaginatorReducers = examinationsPaginator.reducers;
53 | export const examsStoreName = examinationsPaginator.storeName;
54 | export const updateSearchParams = examinationsPaginator.updateSearchParams;
55 |
56 | export const setCurrentPage = examinationsPaginator.setCurrentPage;
57 | export const getCurrentPage = examinationsPaginator.selectors.getCurrentPage;
58 |
59 | //selectors:
60 | export const getTotalElements = examinationsPaginator.selectors.getTotalElements;
61 | export const getPage = examinationsPaginator.selectors.getPage;
62 | export const getSearchParams = examinationsPaginator.selectors.getSearchParams;
63 |
--------------------------------------------------------------------------------
/src/pagination-lib/createPaginator.js:
--------------------------------------------------------------------------------
1 | import { buildFetchingApiActionNames, buildStoreName, convertTotalItemIndexToPageNum } from './utils';
2 | import { loadPage, updatePaginationParams, updateSearchParams, setCurrentPage } from './paginationActions';
3 | import { paginate, entities, paginationParams, searchParamsReducer } from './paginationReducer'
4 | import { getTotalElements, getPage, getPaginationParams, getSearchParams, getCurrentPage, getElementByTotalIndex } from './selectors';
5 | import { combineReducers } from 'redux';
6 | import { currentPageReducer } from './paginationReducer';
7 |
8 | export const createPaginator = (actionName, callApi, config, searchParamsInitState) => {
9 | const actionTypes = buildFetchingApiActionNames(actionName);
10 | const validatedConfig = validateConfig(config);
11 | const storeName = buildStoreName(actionName);
12 |
13 | return {
14 | selectors: {
15 | getTotalElements: getTotalElements(storeName),
16 | getPage: getPage(storeName),
17 | getPageNormalized: getPage(storeName, true),
18 | getPaginationParams: getPaginationParams(storeName),
19 | getSearchParams: getSearchParams(storeName),
20 | getCurrentPage: getCurrentPage(storeName),
21 | getElementByTotalIndex: getElementByTotalIndex(storeName, validatedConfig.elementsPerPage)
22 | },
23 | reducers: combineReducers({
24 | pagination: paginate(actionTypes),
25 | entities: entities(actionTypes),
26 | paginationParams: paginationParams(actionTypes.updatePaginationParams, actionTypes.removeCache),
27 | searchParams: searchParamsReducer(actionTypes.updateSearchParams, searchParamsInitState || {}),
28 | currentPage: currentPageReducer(actionTypes.setCurrentPageNumber)
29 | }),
30 | storeName,
31 | requestPage: loadPage(actionTypes, storeName, callApi, validatedConfig),
32 | updatePaginationParams: updatePaginationParams(actionTypes.updatePaginationParams),
33 | updateSearchParams: updateSearchParams(actionTypes.updateSearchParams),
34 | setCurrentPage: setCurrentPage(actionTypes.setCurrentPageNumber),
35 | convertTotalItemIndexToPageNum: convertTotalItemIndexToPageNum(validatedConfig.elementsPerPage)
36 | }
37 | }
38 |
39 | const validateConfig = (config) => {
40 | const newConfig = {
41 | refreshResultInBackground: true,
42 | timeToRefresh: 5000,
43 | searchHistoryLength: 5,
44 | elementsPerPage: 30,
45 | ...config
46 | }
47 | return newConfig;
48 | }
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/pagination-lib/selectors.js:
--------------------------------------------------------------------------------
1 | import { buildUniqueKey } from './utils';
2 |
3 | export const getTotalElements = (sliceName) => (store, searchParams) => {
4 | const currentStore = store[sliceName];
5 | const validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;
6 | const filtersKey = buildUniqueKey(validatedSearchParams);
7 |
8 | if (currentStore && currentStore.pagination[filtersKey]) {
9 | const elementsCount = currentStore.pagination[filtersKey].elementsCount;
10 | return elementsCount ? elementsCount : 0;
11 | }
12 | return 0;
13 | }
14 |
15 | export const getPage = (sliceName, normalized) => (store, page, searchParams) => {
16 | const currentStore = store[sliceName];
17 | const validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;
18 | const filtersKey = buildUniqueKey(validatedSearchParams);
19 | if (currentStore && currentStore.pagination[filtersKey]) {
20 | const foundPage = currentStore.pagination[filtersKey].pages[page];
21 |
22 | if (foundPage && !normalized) {
23 | const loadedElements = foundPage.elements.map(x => {
24 | return currentStore.entities[x];
25 | })
26 | return { ...foundPage, elements: loadedElements };
27 | }
28 | return foundPage;
29 | }
30 | return null;
31 | }
32 |
33 | export const getElementByTotalIndex = (sliceName, elementsPerPage) => (store, totalIndex, searchParams) => {
34 | const pageNum = Math.floor(totalIndex / elementsPerPage) + 1;
35 | const currentStore = store[sliceName];
36 | const validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;
37 | const page = getPage(sliceName)(store, pageNum, validatedSearchParams);
38 | if (page) {
39 | const { elements, ...rest } = page;
40 | if (page.elements) {
41 | const elementIndex = totalIndex % elementsPerPage;
42 | const element = page.elements[elementIndex];
43 | return { ...element, ...rest };
44 | }
45 | return rest;
46 | }
47 | return null;
48 | }
49 |
50 | export const getPaginationParams = (sliceName) => (store) => {
51 | const currentStore = store[sliceName];
52 | return currentStore.paginationParams;
53 | }
54 |
55 | export const getSearchParams = (sliceName) => (store) => {
56 | const searchParams = store[sliceName].searchParams;
57 | return searchParams;
58 | }
59 |
60 | export const getCurrentPage = (sliceName) => (store) => {
61 | const currentPage = store[sliceName].currentPage;
62 | return currentPage;
63 | }
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/store/updateElement.js:
--------------------------------------------------------------------------------
1 | import { getPage } from './../paginationWithUpdateConfig';
2 | import { provider } from '../../ExaminationsGenerator';
3 |
4 | export const globalEntitiesActionNames = {
5 | entitiesAdded: 'ENTITIES_ADDED',
6 | entityUpdating: 'ENTITY_UPDATING',
7 | entityUpdateSuccess: 'ENTITY_UPDATE_SUCCESS',
8 | entityUpdateFailure: 'ENTITY_UPDATE_FAILURE'
9 | };
10 |
11 | //action creator
12 | export const updateEntities = (entityId, data) => dispatch => {
13 | dispatch({
14 | type: globalEntitiesActionNames.entityUpdating,
15 | id: entityId
16 | });
17 |
18 | const updatedEntity = provider.updateEntity(entityId, data);
19 | setTimeout(() => {
20 | const isResponseSuccessful = true;
21 | if (isResponseSuccessful) {
22 | dispatch({
23 | type: globalEntitiesActionNames.entityUpdateSuccess,
24 | id: entityId,
25 | data: updatedEntity
26 | });
27 | } else {
28 | dispatch({
29 | type: globalEntitiesActionNames.entityUpdateFailure,
30 | id: entityId
31 | });
32 | }
33 | }, 1500);
34 | };
35 |
36 | //reducer
37 | export const globalExaminationsReducer = (state = {}, action) => {
38 | if (state[action.id]) {
39 | const { isUpdating, isUpdatingSuccess, isUpdatingFailure, ...clone } = state[action.id];
40 | switch (action.type) {
41 | case globalEntitiesActionNames.entityUpdating:
42 | return {
43 | ...state,
44 | [action.id]: { ...clone, isUpdating: true }
45 | };
46 | case globalEntitiesActionNames.entityUpdateSuccess:
47 | return {
48 | ...state,
49 | [action.id]: { ...action.data, isUpdatingSuccess: true }
50 | };
51 | case globalEntitiesActionNames.entityUpdateFailure:
52 | return {
53 | ...state,
54 | [action.id]: { ...clone, isUpdatingFailure: true }
55 | };
56 | default:
57 | //continue;
58 | }
59 | }
60 | switch (action.type) {
61 | case globalEntitiesActionNames.entitiesAdded:
62 | return {
63 | ...state,
64 | ...action.entities
65 | };
66 | default:
67 | return state;
68 | }
69 | };
70 |
71 | //selector - denormalize entities
72 | export const getPageWithGlobalElements = (state, chosenPage) => {
73 | const pageWithoutData = getPage(state, chosenPage);
74 |
75 | if (pageWithoutData && pageWithoutData.elements) {
76 | const loadedElements = pageWithoutData.elements.map(x => {
77 | return state.globalEntities[x];
78 | });
79 | return { ...pageWithoutData, elements: loadedElements };
80 | }
81 | return pageWithoutData;
82 | };
83 |
--------------------------------------------------------------------------------
/dist/utils.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/pagination-lib/utils.js"],"names":["buildFetchingApiActionNames","fetching","actionPrefix","success","failure","removeCache","removeSearchResult","refreshing","updatePaginationParams","updateScrollToIndex","updateSearchParams","setCurrentPageNumber","buildStoreName","storePrefix","buildUniqueKey","obj","console","error","result","Object","entries","sort","forEach","key","value","concat","convertTotalItemIndexToPageNum","elementsPerPage","totalIndex","Math","floor"],"mappings":";;;;;;;;AAAO,IAAMA,oEAA8B,SAA9BA,2BAA8B,eAAgB;AACvD,WAAO;AACHC,kBAAaC,YAAb,cADG;AAEHC,iBAAYD,YAAZ,aAFG;AAGHE,iBAAYF,YAAZ,aAHG;AAIHG,qBAAgBH,YAAhB,oBAJG;AAKHI,4BAAuBJ,YAAvB,0BALG;AAMHK,oBAAeL,YAAf,qBANG;AAOHM,gCAA2BN,YAA3B,8BAPG;AAQHO,6BAAwBP,YAAxB,4BARG,EAQ2D;AAC9DQ,4BAAuBR,YAAvB,0BATG;AAUHS,8BAAyBT,YAAzB;AAVG,KAAP;AAYH,CAbM;;AAeA,IAAMU,0CAAiB,SAAjBA,cAAiB,cAAe;AACzC,WAAUC,WAAV;AACH,CAFM;;AAIA,IAAMC,0CAAiB,SAAjBA,cAAiB,CAACC,GAAD,EAAS;AACnC,QAAI,CAACA,GAAL,EAAU;AACNC,gBAAQC,KAAR,CAAc,0BAAd;AACH;;AAED,QAAIC,SAAS,EAAb;AACAC,WAAOC,OAAP,CAAeL,GAAf,EAAoBM,IAApB,GAA2BC,OAA3B,CACI,gBAAkB;AAAA;AAAA,YAAhBC,GAAgB;AAAA,YAAXC,KAAW;;AACdN,iBAASA,OAAOO,MAAP,CAAcF,GAAd,EAAmB,GAAnB,EAAwBC,KAAxB,EAA+B,GAA/B,CAAT;AACH,KAHL;AAKA,WAAON,MAAP;AACH,CAZM;;AAcA,IAAMQ,0EAAiC,SAAjCA,8BAAiC,CAACC,eAAD;AAAA,WAAqB,UAACC,UAAD,EAAgB;AAC/E,eAAOC,KAAKC,KAAL,CAAWF,aAAaD,eAAxB,IAA2C,CAAlD;AACH,KAF6C;AAAA,CAAvC","file":"utils.js","sourcesContent":["export const buildFetchingApiActionNames = actionPrefix => {\r\n return {\r\n fetching: `${actionPrefix}_FETCHING`,\r\n success: `${actionPrefix}_SUCCESS`,\r\n failure: `${actionPrefix}_FAILURE`,\r\n removeCache: `${actionPrefix}_SOURCE_CHANGED`,\r\n removeSearchResult: `${actionPrefix}_REMOVE_SEARCH_RESULT`,\r\n refreshing: `${actionPrefix}_REFRESHING_PAGE`,\r\n updatePaginationParams: `${actionPrefix}_UPDATE_PAGINATION_PARAMS`,\r\n updateScrollToIndex: `${actionPrefix}_UPDATE_SCROLL_TO_INDEX`,//obsolete\r\n updateSearchParams: `${actionPrefix}_UPDATE_SEARCH_PARAMS`,\r\n setCurrentPageNumber: `${actionPrefix}_SET_CURRENT_PAGE`\r\n }\r\n}\r\n\r\nexport const buildStoreName = storePrefix => {\r\n return `${storePrefix}_store`;\r\n}\r\n\r\nexport const buildUniqueKey = (obj) => {\r\n if (!obj) {\r\n console.error('input has to be defined!');\r\n }\r\n\r\n let result = '';\r\n Object.entries(obj).sort().forEach(\r\n ([key, value]) => {\r\n result = result.concat(key, '_', value, '_');\r\n }\r\n );\r\n return result;\r\n}\r\n\r\nexport const convertTotalItemIndexToPageNum = (elementsPerPage) => (totalIndex) => {\r\n return Math.floor(totalIndex / elementsPerPage) + 1;\r\n}\r\n"]}
--------------------------------------------------------------------------------
/src/Demo/StandardPaginationUpdateEntity/paginationWithUpdateConfig.js:
--------------------------------------------------------------------------------
1 | import { createPaginator } from './../../pagination-lib/createPaginator';
2 | import { normalize, schema } from 'normalizr';
3 | import { globalEntitiesActionNames } from './store/updateElement';
4 | import { provider } from '../ExaminationsGenerator';
5 |
6 | export const ELEMENTS_PER_PAGE = 15;
7 |
8 | const callExaminationsApi = (page, requestParams, dispatch) => {
9 | const { searchPhrase } = requestParams;
10 |
11 | return new Promise((resolve, reject) => {
12 | setTimeout(() => {
13 | const serverResponse = provider.getExaminationsPage(page, ELEMENTS_PER_PAGE, searchPhrase);
14 | const formattedResponse = normalizeResponse(serverResponse);
15 | dispatch({
16 | type: globalEntitiesActionNames.entitiesAdded,
17 | entities: formattedResponse.entities
18 | });
19 | const ress = {
20 | totalElements: formattedResponse.totalElements,
21 | elements: formattedResponse.elements,
22 | entities: []
23 | };
24 | resolve(ress);
25 | }, 2000);
26 | });
27 | };
28 |
29 | const examination = new schema.Entity('examinations');
30 | const responseSchema = new schema.Object({ elements: new schema.Array(examination) });
31 |
32 | const normalizeResponse = serverResponse => {
33 | const normalizedData = normalize(serverResponse, responseSchema);
34 | const notNullEntities = normalizedData.entities.examinations ? normalizedData.entities.examinations : [];
35 | return {
36 | totalElements: serverResponse.totalElements, //liczba elementów dla danych parametrów wyszukiwania (searchParams)
37 | elements: normalizedData.result.elements, //idki elementów dla danych parametrów wyszukiwania (page + searchParams)
38 | entities: notNullEntities //znormalizowane parametry z listy powyżej
39 | };
40 | };
41 |
42 | const searchParamsInitState = { searchPhrase: '' };
43 |
44 | //default config below
45 | const config = {
46 | refreshResultInBackground: false,
47 | timeToRefresh: 1000, //[ms]
48 | searchHistoryLength: 5,
49 | elementsPerPage: ELEMENTS_PER_PAGE
50 | };
51 |
52 | const examinationsPaginator = createPaginator('update', callExaminationsApi, config, searchParamsInitState);
53 |
54 | export const paginatorStoreName = examinationsPaginator.storeName;
55 |
56 | //export used methods
57 | export const loadExaminationsPage = examinationsPaginator.requestPage;
58 | export const paginationUpdateReducers = examinationsPaginator.reducers;
59 | export const paginationUpdateStoreName = examinationsPaginator.storeName;
60 | export const updateSearchParams = examinationsPaginator.updateSearchParams;
61 |
62 | export const setCurrentPage = examinationsPaginator.setCurrentPage;
63 | export const getCurrentPage = examinationsPaginator.selectors.getCurrentPage;
64 |
65 | //selectors:
66 | export const getTotalElements = examinationsPaginator.selectors.getTotalElements;
67 | export const getPage = examinationsPaginator.selectors.getPageNormalized;
68 | export const getSearchParams = examinationsPaginator.selectors.getSearchParams;
69 |
--------------------------------------------------------------------------------
/dist/createPaginator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.createPaginator = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _utils = require('./utils');
11 |
12 | var _paginationActions = require('./paginationActions');
13 |
14 | var _paginationReducer = require('./paginationReducer');
15 |
16 | var _selectors = require('./selectors');
17 |
18 | var _redux = require('redux');
19 |
20 | var createPaginator = exports.createPaginator = function createPaginator(actionName, callApi, config, searchParamsInitState) {
21 | var actionTypes = (0, _utils.buildFetchingApiActionNames)(actionName);
22 | var validatedConfig = validateConfig(config);
23 | var storeName = (0, _utils.buildStoreName)(actionName);
24 |
25 | return {
26 | selectors: {
27 | getTotalElements: (0, _selectors.getTotalElements)(storeName),
28 | getPage: (0, _selectors.getPage)(storeName),
29 | getPageNormalized: (0, _selectors.getPage)(storeName, true),
30 | getPaginationParams: (0, _selectors.getPaginationParams)(storeName),
31 | getSearchParams: (0, _selectors.getSearchParams)(storeName),
32 | getCurrentPage: (0, _selectors.getCurrentPage)(storeName),
33 | getElementByTotalIndex: (0, _selectors.getElementByTotalIndex)(storeName, validatedConfig.elementsPerPage)
34 | },
35 | reducers: (0, _redux.combineReducers)({
36 | pagination: (0, _paginationReducer.paginate)(actionTypes),
37 | entities: (0, _paginationReducer.entities)(actionTypes),
38 | paginationParams: (0, _paginationReducer.paginationParams)(actionTypes.updatePaginationParams, actionTypes.removeCache),
39 | searchParams: (0, _paginationReducer.searchParamsReducer)(actionTypes.updateSearchParams, searchParamsInitState || {}),
40 | currentPage: (0, _paginationReducer.currentPageReducer)(actionTypes.setCurrentPageNumber)
41 | }),
42 | storeName: storeName,
43 | requestPage: (0, _paginationActions.loadPage)(actionTypes, storeName, callApi, validatedConfig),
44 | updatePaginationParams: (0, _paginationActions.updatePaginationParams)(actionTypes.updatePaginationParams),
45 | updateSearchParams: (0, _paginationActions.updateSearchParams)(actionTypes.updateSearchParams),
46 | setCurrentPage: (0, _paginationActions.setCurrentPage)(actionTypes.setCurrentPageNumber),
47 | convertTotalItemIndexToPageNum: (0, _utils.convertTotalItemIndexToPageNum)(validatedConfig.elementsPerPage)
48 | };
49 | };
50 |
51 | var validateConfig = function validateConfig(config) {
52 | var newConfig = _extends({
53 | refreshResultInBackground: true,
54 | timeToRefresh: 5000,
55 | searchHistoryLength: 5,
56 | elementsPerPage: 30
57 | }, config);
58 | return newConfig;
59 | };
60 | //# sourceMappingURL=createPaginator.js.map
--------------------------------------------------------------------------------
/dist/selectors.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.getCurrentPage = exports.getSearchParams = exports.getPaginationParams = exports.getElementByTotalIndex = exports.getPage = exports.getTotalElements = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _utils = require('./utils');
11 |
12 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
13 |
14 | var getTotalElements = exports.getTotalElements = function getTotalElements(sliceName) {
15 | return function (store, searchParams) {
16 | var currentStore = store[sliceName];
17 | var validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;
18 | var filtersKey = (0, _utils.buildUniqueKey)(validatedSearchParams);
19 |
20 | if (currentStore && currentStore.pagination[filtersKey]) {
21 | var elementsCount = currentStore.pagination[filtersKey].elementsCount;
22 | return elementsCount ? elementsCount : 0;
23 | }
24 | return 0;
25 | };
26 | };
27 |
28 | var getPage = exports.getPage = function getPage(sliceName, normalized) {
29 | return function (store, page, searchParams) {
30 | var currentStore = store[sliceName];
31 | var validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;
32 | var filtersKey = (0, _utils.buildUniqueKey)(validatedSearchParams);
33 | if (currentStore && currentStore.pagination[filtersKey]) {
34 | var foundPage = currentStore.pagination[filtersKey].pages[page];
35 |
36 | if (foundPage && !normalized) {
37 | var loadedElements = foundPage.elements.map(function (x) {
38 | return currentStore.entities[x];
39 | });
40 | return _extends({}, foundPage, { elements: loadedElements });
41 | }
42 | return foundPage;
43 | }
44 | return null;
45 | };
46 | };
47 |
48 | var getElementByTotalIndex = function getElementByTotalIndex(sliceName, elementsPerPage) {
49 | return function (store, totalIndex, searchParams) {
50 | var pageNum = Math.floor(totalIndex / elementsPerPage) + 1;
51 | var currentStore = store[sliceName];
52 | var validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;
53 | var page = getPage(sliceName)(store, pageNum, validatedSearchParams);
54 | if (page) {
55 | var elements = page.elements,
56 | rest = _objectWithoutProperties(page, ['elements']);
57 |
58 | if (page.elements) {
59 | var elementIndex = totalIndex % elementsPerPage;
60 | var element = page.elements[elementIndex];
61 | return _extends({}, element, rest);
62 | }
63 | return rest;
64 | }
65 | return null;
66 | };
67 | };
68 |
69 | exports.getElementByTotalIndex = getElementByTotalIndex;
70 | var getPaginationParams = exports.getPaginationParams = function getPaginationParams(sliceName) {
71 | return function (store) {
72 | var currentStore = store[sliceName];
73 | return currentStore.paginationParams;
74 | };
75 | };
76 |
77 | var getSearchParams = exports.getSearchParams = function getSearchParams(sliceName) {
78 | return function (store) {
79 | var searchParams = store[sliceName].searchParams;
80 | return searchParams;
81 | };
82 | };
83 |
84 | var getCurrentPage = exports.getCurrentPage = function getCurrentPage(sliceName) {
85 | return function (store) {
86 | var currentPage = store[sliceName].currentPage;
87 | return currentPage;
88 | };
89 | };
90 | //# sourceMappingURL=selectors.js.map
--------------------------------------------------------------------------------
/dist/createPaginator.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/pagination-lib/createPaginator.js"],"names":["createPaginator","actionName","callApi","config","searchParamsInitState","actionTypes","validatedConfig","validateConfig","storeName","selectors","getTotalElements","getPage","getPageNormalized","getPaginationParams","getSearchParams","getCurrentPage","getElementByTotalIndex","elementsPerPage","reducers","pagination","entities","paginationParams","updatePaginationParams","removeCache","searchParams","updateSearchParams","currentPage","setCurrentPageNumber","requestPage","setCurrentPage","convertTotalItemIndexToPageNum","newConfig","refreshResultInBackground","timeToRefresh","searchHistoryLength"],"mappings":";;;;;;;;;AAAA;;AACA;;AACA;;AACA;;AACA;;AAGO,IAAMA,4CAAkB,SAAlBA,eAAkB,CAACC,UAAD,EAAaC,OAAb,EAAsBC,MAAtB,EAA8BC,qBAA9B,EAAwD;AACnF,QAAMC,cAAc,wCAA4BJ,UAA5B,CAApB;AACA,QAAMK,kBAAkBC,eAAeJ,MAAf,CAAxB;AACA,QAAMK,YAAY,2BAAeP,UAAf,CAAlB;;AAEA,WAAO;AACHQ,mBAAW;AACPC,8BAAkB,iCAAiBF,SAAjB,CADX;AAEPG,qBAAS,wBAAQH,SAAR,CAFF;AAGPI,+BAAmB,wBAAQJ,SAAR,EAAmB,IAAnB,CAHZ;AAIPK,iCAAqB,oCAAoBL,SAApB,CAJd;AAKPM,6BAAiB,gCAAgBN,SAAhB,CALV;AAMPO,4BAAgB,+BAAeP,SAAf,CANT;AAOPQ,oCAAwB,uCAAuBR,SAAvB,EAAkCF,gBAAgBW,eAAlD;AAPjB,SADR;AAUHC,kBAAU,4BAAgB;AACtBC,wBAAY,iCAASd,WAAT,CADU;AAEtBe,sBAAU,iCAASf,WAAT,CAFY;AAGtBgB,8BAAkB,yCAAiBhB,YAAYiB,sBAA7B,EAAqDjB,YAAYkB,WAAjE,CAHI;AAItBC,0BAAc,4CAAoBnB,YAAYoB,kBAAhC,EAAoDrB,yBAAyB,EAA7E,CAJQ;AAKtBsB,yBAAa,2CAAmBrB,YAAYsB,oBAA/B;AALS,SAAhB,CAVP;AAiBHnB,4BAjBG;AAkBHoB,qBAAa,iCAASvB,WAAT,EAAsBG,SAAtB,EAAiCN,OAAjC,EAA0CI,eAA1C,CAlBV;AAmBHgB,gCAAwB,+CAAuBjB,YAAYiB,sBAAnC,CAnBrB;AAoBHG,4BAAoB,2CAAmBpB,YAAYoB,kBAA/B,CApBjB;AAqBHI,wBAAgB,uCAAexB,YAAYsB,oBAA3B,CArBb;AAsBHG,wCAAgC,2CAA+BxB,gBAAgBW,eAA/C;AAtB7B,KAAP;AAwBH,CA7BM;;AA+BP,IAAMV,iBAAiB,SAAjBA,cAAiB,CAACJ,MAAD,EAAY;AAC/B,QAAM4B;AACFC,mCAA2B,IADzB;AAEFC,uBAAe,IAFb;AAGFC,6BAAqB,CAHnB;AAIFjB,yBAAiB;AAJf,OAKCd,MALD,CAAN;AAOA,WAAO4B,SAAP;AACH,CATD","file":"createPaginator.js","sourcesContent":["import { buildFetchingApiActionNames, buildStoreName, convertTotalItemIndexToPageNum } from './utils';\r\nimport { loadPage, updatePaginationParams, updateSearchParams, setCurrentPage } from './paginationActions';\r\nimport { paginate, entities, paginationParams, searchParamsReducer } from './paginationReducer'\r\nimport { getTotalElements, getPage, getPaginationParams, getSearchParams, getCurrentPage, getElementByTotalIndex } from './selectors';\r\nimport { combineReducers } from 'redux';\r\nimport { currentPageReducer } from './paginationReducer';\r\n\r\nexport const createPaginator = (actionName, callApi, config, searchParamsInitState) => {\r\n const actionTypes = buildFetchingApiActionNames(actionName);\r\n const validatedConfig = validateConfig(config);\r\n const storeName = buildStoreName(actionName);\r\n\r\n return {\r\n selectors: {\r\n getTotalElements: getTotalElements(storeName),\r\n getPage: getPage(storeName),\r\n getPageNormalized: getPage(storeName, true),\r\n getPaginationParams: getPaginationParams(storeName),\r\n getSearchParams: getSearchParams(storeName),\r\n getCurrentPage: getCurrentPage(storeName),\r\n getElementByTotalIndex: getElementByTotalIndex(storeName, validatedConfig.elementsPerPage)\r\n },\r\n reducers: combineReducers({\r\n pagination: paginate(actionTypes),\r\n entities: entities(actionTypes),\r\n paginationParams: paginationParams(actionTypes.updatePaginationParams, actionTypes.removeCache),\r\n searchParams: searchParamsReducer(actionTypes.updateSearchParams, searchParamsInitState || {}),\r\n currentPage: currentPageReducer(actionTypes.setCurrentPageNumber)\r\n }),\r\n storeName,\r\n requestPage: loadPage(actionTypes, storeName, callApi, validatedConfig),\r\n updatePaginationParams: updatePaginationParams(actionTypes.updatePaginationParams),\r\n updateSearchParams: updateSearchParams(actionTypes.updateSearchParams),\r\n setCurrentPage: setCurrentPage(actionTypes.setCurrentPageNumber),\r\n convertTotalItemIndexToPageNum: convertTotalItemIndexToPageNum(validatedConfig.elementsPerPage)\r\n }\r\n}\r\n\r\nconst validateConfig = (config) => {\r\n const newConfig = {\r\n refreshResultInBackground: true,\r\n timeToRefresh: 5000,\r\n searchHistoryLength: 5,\r\n elementsPerPage: 30,\r\n ...config\r\n }\r\n return newConfig;\r\n}"]}
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/pagination-lib/paginationReducer.js:
--------------------------------------------------------------------------------
1 | import dotProp from 'dot-prop-immutable';
2 |
3 | export const paginate = (types) => {
4 | const { fetching, success, failure, refreshing, removeCache, removeSearchResult } = types;
5 |
6 | return (state = {}, action) => {
7 | const stateToUpdate = dotProp.set(state, `${action.filtersKey}.lastUpdateTime`, Date.now());
8 | switch (action.type) {
9 | case fetching:
10 | return dotProp.set(stateToUpdate, `${action.filtersKey}.pages.${action.page}`,
11 | {
12 | isFetching: true,
13 | isRefreshing: false,
14 | isSuccess: false,
15 | isFailed: false,
16 | lastUpdateTime: Date.now(),
17 | elements: []
18 | }
19 | );
20 | case success:
21 | const withElemsCount = dotProp.merge(stateToUpdate, `${action.filtersKey}`,
22 | {
23 | elementsCount: action.response.totalElements
24 | });
25 | return dotProp.set(withElemsCount, `${action.filtersKey}.pages.${action.page}`,
26 | {
27 | isFetching: false,
28 | isRefreshing: false,
29 | isSuccess: true,
30 | isFailed: false,
31 | lastUpdateTime: Date.now(),
32 | elements: action.response.elements
33 | }
34 | );
35 | case failure:
36 | return dotProp.set(stateToUpdate, `${action.filtersKey}.pages.${action.page}`,
37 | {
38 | isFetching: false,
39 | isRefreshing: false,
40 | isSuccess: false,
41 | isFailed: true,
42 | lastUpdateTime: Date.now(),
43 | elements: []
44 | }
45 | );
46 | case removeCache:
47 | return {};
48 | case refreshing:
49 | return dotProp.merge(stateToUpdate, `${action.filtersKey}.pages.${action.page}`,
50 | {
51 | isFetching: false,
52 | isRefreshing: true,
53 | isSuccess: false,
54 | isFailed: false,
55 | lastUpdateTime: Date.now()
56 | });
57 | case removeSearchResult:
58 | return dotProp.delete(stateToUpdate, `${action.filtersKey}`);
59 | default:
60 | return state;
61 | }
62 | }
63 | }
64 |
65 | export const entities = (actionTypes) => {
66 | const { success, removeCache } = actionTypes;
67 | return (state = {}, action) => {
68 | switch (action.type) {
69 | case success:
70 | return {
71 | ...state,
72 | ...action.response.entities
73 | }
74 | case removeCache:
75 | return {};
76 | default:
77 | return state;
78 | }
79 | }
80 | }
81 |
82 | export const paginationParams = (actionType, removeCacheActionType) => {
83 | const updatePaginationParams = actionType;
84 | const removeCache = removeCacheActionType;
85 | return (state = {}, action) => {
86 | switch (action.type) {
87 | case updatePaginationParams:
88 | return { ...state, ...action.paginationParams };
89 | case removeCache:
90 | return {};
91 | default:
92 | return state;
93 | }
94 | }
95 | }
96 |
97 | export const searchParamsReducer = (searchParamsActionType, initState) => {
98 | return (state = initState, action) => {
99 | switch (action.type) {
100 | case searchParamsActionType:
101 | return { ...state, ...action.searchParams };
102 | default:
103 | return state;
104 | }
105 | }
106 | }
107 |
108 | export const currentPageReducer = (setCurrentPageNumberActionType) => {
109 | return (state = { pageNumber: 1 }, action) => {
110 | switch (action.type) {
111 | case setCurrentPageNumberActionType:
112 | return {
113 | pageNumber: action.pageNumber
114 | };
115 | default:
116 | return state;
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/dist/selectors.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/pagination-lib/selectors.js"],"names":["getTotalElements","sliceName","store","searchParams","currentStore","validatedSearchParams","filtersKey","pagination","elementsCount","getPage","normalized","page","foundPage","pages","loadedElements","elements","map","entities","x","getElementByTotalIndex","elementsPerPage","totalIndex","pageNum","Math","floor","rest","elementIndex","element","getPaginationParams","paginationParams","getSearchParams","getCurrentPage","currentPage"],"mappings":"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","file":"selectors.js","sourcesContent":["import { buildUniqueKey } from './utils';\r\n\r\nexport const getTotalElements = (sliceName) => (store, searchParams) => {\r\n const currentStore = store[sliceName];\r\n const validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;\r\n const filtersKey = buildUniqueKey(validatedSearchParams);\r\n\r\n if (currentStore && currentStore.pagination[filtersKey]) {\r\n const elementsCount = currentStore.pagination[filtersKey].elementsCount;\r\n return elementsCount ? elementsCount : 0;\r\n }\r\n return 0;\r\n}\r\n\r\nexport const getPage = (sliceName, normalized) => (store, page, searchParams) => {\r\n const currentStore = store[sliceName];\r\n const validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;\r\n const filtersKey = buildUniqueKey(validatedSearchParams);\r\n if (currentStore && currentStore.pagination[filtersKey]) {\r\n const foundPage = currentStore.pagination[filtersKey].pages[page];\r\n\r\n if (foundPage && !normalized) {\r\n const loadedElements = foundPage.elements.map(x => {\r\n return currentStore.entities[x];\r\n })\r\n return { ...foundPage, elements: loadedElements };\r\n }\r\n return foundPage;\r\n }\r\n return null;\r\n}\r\n\r\nexport const getElementByTotalIndex = (sliceName, elementsPerPage) => (store, totalIndex, searchParams) => {\r\n const pageNum = Math.floor(totalIndex / elementsPerPage) + 1;\r\n const currentStore = store[sliceName];\r\n const validatedSearchParams = searchParams ? searchParams : currentStore.searchParams;\r\n const page = getPage(sliceName)(store, pageNum, validatedSearchParams);\r\n if (page) {\r\n const { elements, ...rest } = page;\r\n if (page.elements) {\r\n const elementIndex = totalIndex % elementsPerPage;\r\n const element = page.elements[elementIndex];\r\n return { ...element, ...rest };\r\n }\r\n return rest;\r\n }\r\n return null;\r\n}\r\n\r\nexport const getPaginationParams = (sliceName) => (store) => {\r\n const currentStore = store[sliceName];\r\n return currentStore.paginationParams;\r\n}\r\n\r\nexport const getSearchParams = (sliceName) => (store) => {\r\n const searchParams = store[sliceName].searchParams;\r\n return searchParams;\r\n}\r\n\r\nexport const getCurrentPage = (sliceName) => (store) => {\r\n const currentPage = store[sliceName].currentPage;\r\n return currentPage;\r\n}"]}
--------------------------------------------------------------------------------
/src/pagination-lib/VirtualizedListWrapper/VirtualList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { List, InfiniteLoader, AutoSizer } from 'react-virtualized';
3 | import { connect } from 'react-redux';
4 | import PropTypes from 'prop-types';
5 | import { bindActionCreators } from 'redux';
6 | import { buildUniqueKey } from './../utils';
7 | import styled from 'styled-components';
8 |
9 | const AutoSizerContainer = styled.div`
10 | flex: 1 1 auto;
11 | `;
12 |
13 | const VirtualListWithoutOutline = styled(List)`
14 | outline: none;
15 | `;
16 |
17 | const mapStateToProps = (state, props) => {
18 | const totalElements = props.paginator.selectors.getTotalElements(state);
19 | const notZeroRowCount = totalElements > 0 ? totalElements : 1;
20 | return {
21 | paginationParams: props.paginator.selectors.getPaginationParams(state),
22 | searchParams: props.paginator.selectors.getSearchParams(state),
23 | notZeroRowCount
24 | }
25 | }
26 |
27 | const mapDispatchToProps = (dispatch, props) => {
28 | return bindActionCreators({
29 | updatePaginationParams: props.paginator.updatePaginationParams,
30 | requestPage: props.paginator.requestPage
31 | }, dispatch);
32 | }
33 |
34 | class VirtualList extends Component {
35 |
36 | constructor(props) {
37 | super(props);
38 | this.isRowLoaded = this.isRowLoaded.bind(this);
39 | this.noRowsRenderer = this.noRowsRenderer.bind(this);
40 | this.loadMoreRows = this.loadMoreRows.bind(this);
41 | this.rowRenderer = this.rowRenderer.bind(this);
42 | this.onRowClick = this.onRowClick.bind(this);
43 | this.state = { scrollToIndex: 0 }
44 | this.prevSearchParamsKey = buildUniqueKey(props.searchParams);
45 | }
46 |
47 | loadMoreRows({ startIndex, stopIndex }) {
48 | //nop
49 | }
50 |
51 | isRowLoaded({ index }) {
52 | return false;
53 | }
54 |
55 | noRowsRenderer() {
56 | return (no rows
);
57 | }
58 |
59 | onRowClick(index) {
60 | this.props.updatePaginationParams({ selectedIndex: index, force: false });
61 | }
62 |
63 | rowRenderer({ index, key, style }) {
64 | const pageNum = this.props.paginator.convertTotalItemIndexToPageNum(index);
65 | this.props.requestPage(pageNum);
66 | return (
67 | { this.onRowClick(index) }}>
68 | {this.props.rowRenderer(index)}
69 |
);
70 | }
71 |
72 | componentWillReceiveProps(nextProps) {
73 | const { selectedIndex, force } = nextProps.paginationParams;
74 | if (force) {
75 | this.setState({ scrollToIndex: selectedIndex });
76 | }
77 |
78 | const searchParamsKey = buildUniqueKey(nextProps.searchParams);
79 | if (searchParamsKey !== this.prevSearchParamsKey) {
80 | this.props.updatePaginationParams({ selectedIndex: null, force: true });
81 | this.prevSearchParamsKey = searchParamsKey;
82 | }
83 | }
84 |
85 | componentWillMount() {
86 | this.setState({ scrollToIndex: this.props.paginationParams.selectedIndex });
87 | }
88 |
89 | render() {
90 | const scrollToIndexZeroBased = this.state.scrollToIndex;
91 | return (
92 |
93 |
94 | {({ width, height }) => (
95 |
100 | {({ onRowsRendered, registerChild }) => {
101 | return (
102 | );
114 | }
115 | }
116 |
117 | )}
118 |
119 |
120 | );
121 | }
122 | }
123 |
124 | VirtualList.propTypes = {
125 | rowRenderer: PropTypes.func.isRequired,
126 | rowHeight: PropTypes.number.isRequired,
127 | paginator: PropTypes.object.isRequired
128 | };
129 |
130 | export default connect(mapStateToProps, mapDispatchToProps)(VirtualList);
131 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Redux-cached-pagination
2 |
3 | Simplify and store pagination in react-redux applications
4 |
5 | ## Installation
6 |
7 | ```
8 | npm install redux-cached-pagination --save
9 | ```
10 |
11 | ## Features
12 |
13 | `Redux-cached-pagination` is a set of action creators and reducers to easily improve quality of pagination in your react-redux application.
14 | Simple configuration gives you possibility to achieve advanced pagination features:
15 |
16 | * extremely improved pagination UI user experience
17 | * storing normalized data in Redux
18 | * caching pages based on search filters
19 | * simplified react-virtualized list usage
20 | * refreshing results in background
21 | * storing last visited page and used filters
22 | Demo page implements all above features.
23 |
24 | ## Demo
25 |
26 | Demo is available at [DEMO](https://tranzystor.github.io/redux-cached-pagination/)
27 |
28 | To run demo locally:
29 |
30 | ```
31 | npm run start
32 | ```
33 |
34 | ## Usage
35 |
36 | `Redux-cached-pagination` depends on redux and redux-thunk. You should look at the Demo folder before trying to use this.
37 | In order to use `redux-cached-pagination` you have to implement fetching method with signature like that:
38 |
39 | ```
40 | const callExaminationsApi = (page, requestParams) => {
41 | const { searchPhrase } = requestParams;
42 | return new Promise((resolve, reject) => {
43 | setTimeout(() => {
44 | const serverResponse = provider.getExaminationsPage(page, ELEMENTS_PER_PAGE, searchPhrase);
45 | const formattedResponse = normalizeResponse(serverResponse);
46 | resolve(formattedResponse);
47 | }, 1500);
48 | });
49 | };
50 | ```
51 |
52 | `page` - number of required page
53 | `requestParams` - fully customizable search parameters
54 |
55 | Fetching method should return Promise because managing state is based on that.
56 |
57 | Fetching method has to return normalized data which is stored efficiently in Redux. It returns object with required fields:
58 |
59 | * `totalElements` - the number of all rows
60 | * `elements` - list of elements for current request params. it's normalized
61 | * `entities` - not null entities for current request params
62 |
63 | To get more info about normalization visit: https://github.com/paularmstrong/normalizr
64 |
65 | In order to create paginator use 'createPaginator' method with arguments:
66 |
67 | * `actionNamePrefix` - prefix of generated redux actions
68 | * `callExaminationApi` - function which fetch data from server
69 | * `config` - simple configuration explained below. If null, default config provided
70 | * `searchParamsInitState` - default request params stored in reducer
71 |
72 | Default pagination config looks the same as:
73 |
74 | ```
75 | const config = {
76 | refreshResultInBackground: true,
77 | timeToRefresh: 5000,
78 | searchHistoryLength: 5,
79 | elementsPerPage: 30,
80 | };
81 | ```
82 |
83 | * `refreshResultInBackground` - on/off refreshing search results in background
84 | * `timeToRefresh` - if search result is older than given time and user call the same page again it would be refreshed
85 | * `searchHistoryLength` - determines how many search results are memoized
86 | * `elementsPerPage` - determines length of single page
87 |
88 | `Redux-cached-pagination` provides reducers thus it's required to connect with redux store:
89 |
90 | ```
91 | import { paginationReducers, paginationStoreName } from './paginationConfig';
92 | const rootReducer = combineReducers({
93 | [paginationStoreName]: paginationReducers
94 | });
95 | ```
96 |
97 | created paginator instance provides set of selectors which help you to use `redux-cached-pagination` in several use cases.
98 |
99 | ## API
100 |
101 | Paginator instance provides:
102 |
103 | * `requestPage(page, searchParams)` - the most important function which executes pagination logic. Call this method when page in cache is/will be needed.
104 | Page param is a number of required page. searchParams is object which would be passed to fetching method and used to store data in cache. Used in standard
105 | pagination case only.
106 | * `reducers` - it has to be added to combineReducers method (explained above)
107 | * `examsStoreName` - reducer name (used in combineReducers method)
108 | * `updateSearchParams`, `getSearchParams` - store your searchParams in redux. It helps to recover list state. Calling updateSearch doesn't call fetching method.
109 |
110 | * `setCurrentPage`, `getCurrentPage` - store chosen page number in redux. Call this method to store page number in redux. It's used only in standard pagination case.
111 |
112 | * `getPaginationParams`, `updatePaginationParams` - redux slice used to store another parameters connected with pagination. It's a place to store custom data. In DEMO application (virtual case) this method is used to store selected row index.
113 |
114 | * `getTotalElements` - selector provides total elements count for current search params.
115 | * `getPage` - selector provides page content by given page number. If page doesn't exist, it returns null and doesn't fetch page.
116 |
117 | ## Standard pagination demo
118 |
119 | At first glance it seems to be extremely simple feature but navigation back to the previous page doesn't call fetching method so content is displayed immediately.
120 | It's possible to refresh data in the background. Search list state is stored in redux so navigation doesn't clean search phrase and selected page.
121 |
122 | ## Standard pagination with entity update
123 |
124 | The same example as above but in this case custom reducer is provided. This kind of solution allows to add own actions on entities like updating single entity.
125 |
126 | ## Virtualized pagination
127 |
128 | Additional layer to `react-virtualized` list control.
129 |
130 | ## License
131 |
132 | `redux-cached-pagination` is available under MIT License.
133 |
--------------------------------------------------------------------------------
/dist/paginationReducer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.currentPageReducer = exports.searchParamsReducer = exports.paginationParams = exports.entities = exports.paginate = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _dotPropImmutable = require('dot-prop-immutable');
11 |
12 | var _dotPropImmutable2 = _interopRequireDefault(_dotPropImmutable);
13 |
14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15 |
16 | var paginate = exports.paginate = function paginate(types) {
17 | var fetching = types.fetching,
18 | success = types.success,
19 | failure = types.failure,
20 | refreshing = types.refreshing,
21 | removeCache = types.removeCache,
22 | removeSearchResult = types.removeSearchResult;
23 |
24 |
25 | return function () {
26 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
27 | var action = arguments[1];
28 |
29 | var stateToUpdate = _dotPropImmutable2.default.set(state, action.filtersKey + '.lastUpdateTime', Date.now());
30 | switch (action.type) {
31 | case fetching:
32 | return _dotPropImmutable2.default.set(stateToUpdate, action.filtersKey + '.pages.' + action.page, {
33 | isFetching: true,
34 | isRefreshing: false,
35 | isSuccess: false,
36 | isFailed: false,
37 | lastUpdateTime: Date.now(),
38 | elements: []
39 | });
40 | case success:
41 | var withElemsCount = _dotPropImmutable2.default.merge(stateToUpdate, '' + action.filtersKey, {
42 | elementsCount: action.response.totalElements
43 | });
44 | return _dotPropImmutable2.default.set(withElemsCount, action.filtersKey + '.pages.' + action.page, {
45 | isFetching: false,
46 | isRefreshing: false,
47 | isSuccess: true,
48 | isFailed: false,
49 | lastUpdateTime: Date.now(),
50 | elements: action.response.elements
51 | });
52 | case failure:
53 | return _dotPropImmutable2.default.set(stateToUpdate, action.filtersKey + '.pages.' + action.page, {
54 | isFetching: false,
55 | isRefreshing: false,
56 | isSuccess: false,
57 | isFailed: true,
58 | lastUpdateTime: Date.now(),
59 | elements: []
60 | });
61 | case removeCache:
62 | return {};
63 | case refreshing:
64 | return _dotPropImmutable2.default.merge(stateToUpdate, action.filtersKey + '.pages.' + action.page, {
65 | isFetching: false,
66 | isRefreshing: true,
67 | isSuccess: false,
68 | isFailed: false,
69 | lastUpdateTime: Date.now()
70 | });
71 | case removeSearchResult:
72 | return _dotPropImmutable2.default.delete(stateToUpdate, '' + action.filtersKey);
73 | default:
74 | return state;
75 | }
76 | };
77 | };
78 |
79 | var entities = exports.entities = function entities(actionTypes) {
80 | var success = actionTypes.success,
81 | removeCache = actionTypes.removeCache;
82 |
83 | return function () {
84 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
85 | var action = arguments[1];
86 |
87 | switch (action.type) {
88 | case success:
89 | return _extends({}, state, action.response.entities);
90 | case removeCache:
91 | return {};
92 | default:
93 | return state;
94 | }
95 | };
96 | };
97 |
98 | var paginationParams = exports.paginationParams = function paginationParams(actionType, removeCacheActionType) {
99 | var updatePaginationParams = actionType;
100 | var removeCache = removeCacheActionType;
101 | return function () {
102 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
103 | var action = arguments[1];
104 |
105 | switch (action.type) {
106 | case updatePaginationParams:
107 | return _extends({}, state, action.paginationParams);
108 | case removeCache:
109 | return {};
110 | default:
111 | return state;
112 | }
113 | };
114 | };
115 |
116 | var searchParamsReducer = exports.searchParamsReducer = function searchParamsReducer(searchParamsActionType, initState) {
117 | return function () {
118 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initState;
119 | var action = arguments[1];
120 |
121 | switch (action.type) {
122 | case searchParamsActionType:
123 | return _extends({}, state, action.searchParams);
124 | default:
125 | return state;
126 | }
127 | };
128 | };
129 |
130 | var currentPageReducer = exports.currentPageReducer = function currentPageReducer(setCurrentPageNumberActionType) {
131 | return function () {
132 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { pageNumber: 1 };
133 | var action = arguments[1];
134 |
135 | switch (action.type) {
136 | case setCurrentPageNumberActionType:
137 | return {
138 | pageNumber: action.pageNumber
139 | };
140 | default:
141 | return state;
142 | }
143 | };
144 | };
145 | //# sourceMappingURL=paginationReducer.js.map
--------------------------------------------------------------------------------
/src/pagination-lib/paginationActions.js:
--------------------------------------------------------------------------------
1 |
2 | import { buildUniqueKey } from './utils';
3 |
4 | export const loadPage = (actionTypes, storeName, callApi, config) => {
5 | const { fetching,
6 | success,
7 | failure,
8 | removeCache,
9 | refreshing,
10 | removeSearchResult } = actionTypes;
11 | const { searchHistoryLength } = config;
12 |
13 | return (page, searchParams) =>
14 | (dispatch, getState) => {
15 |
16 | const store = getState()[storeName];
17 | const entryToRemove = getEntryKeyToRemove(store, searchHistoryLength);
18 | if (entryToRemove) {
19 | dispatch({
20 | type: removeSearchResult,
21 | filtersKey: entryToRemove
22 | });
23 | }
24 |
25 | const validSearchParams = getValidSearchParams(searchParams, store);
26 |
27 | const filtersKey = buildUniqueKey(validSearchParams);
28 | const cachedResult = getPage(getState()[storeName], filtersKey, page);
29 | if (shouldAbandonAction(cachedResult, config)) {
30 | return Promise.resolve();
31 | }
32 |
33 | if (!cachedResult) {
34 | dispatch({
35 | type: fetching,
36 | filtersKey: filtersKey,
37 | page: page
38 | });
39 | } else {
40 | dispatch({
41 | type: refreshing,
42 | filtersKey: filtersKey,
43 | page: page
44 | });
45 | }
46 |
47 | return callApi(page, validSearchParams, dispatch, getState).then(
48 | response => {
49 | if (!isResponseValid(response)) {
50 | dispatch({
51 | type: failure,
52 | filtersKey: filtersKey,
53 | page: page
54 | });
55 | return Promise.reject('server response is not valid');
56 | }
57 |
58 | const newTotalCount = response.totalElements;
59 | let actualTotalCount = undefined;
60 | if (getState()[storeName].pagination[filtersKey]) {
61 | actualTotalCount = getState()[storeName].pagination[filtersKey].elementsCount;
62 | }
63 |
64 | if (actualTotalCount && actualTotalCount !== newTotalCount) {
65 | dispatch({
66 | type: removeCache
67 | })
68 | }
69 | dispatch({
70 | type: success,
71 | filtersKey: filtersKey,
72 | page: page,
73 | response: response
74 | });
75 | },
76 | error => {
77 | dispatch({
78 | type: failure,
79 | filtersKey: filtersKey,
80 | page: page
81 | });
82 | return Promise.reject(error);
83 | }
84 | )
85 | }
86 | }
87 |
88 | const getValidSearchParams = (searchParams, store) => {
89 | if (!searchParams) {
90 | return store.searchParams;
91 | }
92 | return searchParams;
93 | }
94 |
95 | const getEntryKeyToRemove = (store, searchHistoryLength) => {
96 | const paginationEntries = Object.entries(store.pagination);
97 | if (paginationEntries.length > searchHistoryLength) {
98 |
99 | const min = paginationEntries.map(([key, val]) => {
100 | return { key: key, time: val.lastUpdateTime }
101 | }).reduce((min, curr) => {
102 | if (!min) {
103 | return curr;
104 | }
105 | if (curr.time < min.time) {
106 | return curr;
107 | }
108 | return min;
109 | }, null);
110 | return min.key
111 | }
112 | return null;
113 | }
114 |
115 | const shouldAbandonAction = (cachedResult, config) => {
116 | if (!cachedResult) {
117 | return false;
118 | }
119 |
120 | const { refreshResultInBackground, timeToRefresh } = config;
121 | if (cachedResult.isFetching || !refreshResultInBackground) {
122 | return true;
123 | }
124 |
125 | if (isTimeToRefresh(cachedResult.lastUpdateTime, timeToRefresh)) {
126 | return false;
127 | }
128 | return true;
129 | }
130 |
131 | const isTimeToRefresh = (prevTime, diff) => {
132 | const result = Date.now() - prevTime > diff;
133 | return result;
134 | }
135 |
136 | const getPage = (store, filtersKey, page) => {
137 | if (store && store.pagination && store.pagination[filtersKey] &&
138 | store.pagination[filtersKey].pages) {
139 | const foundPage = store.pagination[filtersKey].pages[page];
140 | return foundPage;
141 | }
142 | return null;
143 | }
144 |
145 | const isResponseValid = (response) => {
146 | const { totalElements, elements, entities } = response;
147 |
148 | if (Number.isInteger(totalElements) && totalElements === 0) {
149 | return true;
150 | }
151 |
152 | return totalElements && elements && entities;
153 | }
154 |
155 | export const updatePaginationParams = (updatePaginationParams) => {
156 | return (paramsToStore) => (dispatch, getState) => {
157 | dispatch({
158 | type: updatePaginationParams,
159 | paginationParams: paramsToStore
160 | });
161 | }
162 | }
163 |
164 | export const updateSearchParams = (updateSearchParamsActionType) => {
165 | return (searchParamsToStore) => (dispatch, getState) => {
166 | dispatch({
167 | type: updateSearchParamsActionType,
168 | searchParams: searchParamsToStore
169 | });
170 | }
171 | }
172 |
173 | export const setCurrentPage = (setCurrentPageNumberActionType) => {
174 | return (currentPageNumber) => (dispatch, getState) => {
175 | dispatch({
176 | type: setCurrentPageNumberActionType,
177 | pageNumber: currentPageNumber
178 | });
179 | }
180 | }
181 |
182 |
--------------------------------------------------------------------------------
/dist/paginationReducer.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/pagination-lib/paginationReducer.js"],"names":["paginate","types","fetching","success","failure","refreshing","removeCache","removeSearchResult","state","action","stateToUpdate","set","filtersKey","Date","now","type","page","isFetching","isRefreshing","isSuccess","isFailed","lastUpdateTime","elements","withElemsCount","merge","elementsCount","response","totalElements","delete","entities","actionTypes","paginationParams","actionType","removeCacheActionType","updatePaginationParams","searchParamsReducer","searchParamsActionType","initState","searchParams","currentPageReducer","setCurrentPageNumberActionType","pageNumber"],"mappings":"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","file":"paginationReducer.js","sourcesContent":["import dotProp from 'dot-prop-immutable';\r\n\r\nexport const paginate = (types) => {\r\n const { fetching, success, failure, refreshing, removeCache, removeSearchResult } = types;\r\n\r\n return (state = {}, action) => {\r\n const stateToUpdate = dotProp.set(state, `${action.filtersKey}.lastUpdateTime`, Date.now());\r\n switch (action.type) {\r\n case fetching:\r\n return dotProp.set(stateToUpdate, `${action.filtersKey}.pages.${action.page}`,\r\n {\r\n isFetching: true,\r\n isRefreshing: false,\r\n isSuccess: false,\r\n isFailed: false,\r\n lastUpdateTime: Date.now(),\r\n elements: []\r\n }\r\n );\r\n case success:\r\n const withElemsCount = dotProp.merge(stateToUpdate, `${action.filtersKey}`,\r\n {\r\n elementsCount: action.response.totalElements\r\n });\r\n return dotProp.set(withElemsCount, `${action.filtersKey}.pages.${action.page}`,\r\n {\r\n isFetching: false,\r\n isRefreshing: false,\r\n isSuccess: true,\r\n isFailed: false,\r\n lastUpdateTime: Date.now(),\r\n elements: action.response.elements\r\n }\r\n );\r\n case failure:\r\n return dotProp.set(stateToUpdate, `${action.filtersKey}.pages.${action.page}`,\r\n {\r\n isFetching: false,\r\n isRefreshing: false,\r\n isSuccess: false,\r\n isFailed: true,\r\n lastUpdateTime: Date.now(),\r\n elements: []\r\n }\r\n );\r\n case removeCache:\r\n return {};\r\n case refreshing:\r\n return dotProp.merge(stateToUpdate, `${action.filtersKey}.pages.${action.page}`,\r\n {\r\n isFetching: false,\r\n isRefreshing: true,\r\n isSuccess: false,\r\n isFailed: false,\r\n lastUpdateTime: Date.now()\r\n });\r\n case removeSearchResult:\r\n return dotProp.delete(stateToUpdate, `${action.filtersKey}`);\r\n default:\r\n return state;\r\n }\r\n }\r\n}\r\n\r\nexport const entities = (actionTypes) => {\r\n const { success, removeCache } = actionTypes;\r\n return (state = {}, action) => {\r\n switch (action.type) {\r\n case success:\r\n return {\r\n ...state,\r\n ...action.response.entities\r\n }\r\n case removeCache:\r\n return {};\r\n default:\r\n return state;\r\n }\r\n }\r\n}\r\n\r\nexport const paginationParams = (actionType, removeCacheActionType) => {\r\n const updatePaginationParams = actionType;\r\n const removeCache = removeCacheActionType;\r\n return (state = {}, action) => {\r\n switch (action.type) {\r\n case updatePaginationParams:\r\n return { ...state, ...action.paginationParams };\r\n case removeCache:\r\n return {};\r\n default:\r\n return state;\r\n }\r\n }\r\n}\r\n\r\nexport const searchParamsReducer = (searchParamsActionType, initState) => {\r\n return (state = initState, action) => {\r\n switch (action.type) {\r\n case searchParamsActionType:\r\n return { ...state, ...action.searchParams };\r\n default:\r\n return state;\r\n }\r\n }\r\n}\r\n\r\nexport const currentPageReducer = (setCurrentPageNumberActionType) => {\r\n return (state = { pageNumber: 1 }, action) => {\r\n switch (action.type) {\r\n case setCurrentPageNumberActionType:\r\n return {\r\n pageNumber: action.pageNumber\r\n };\r\n default:\r\n return state;\r\n }\r\n }\r\n}"]}
--------------------------------------------------------------------------------
/dist/paginationActions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.setCurrentPage = exports.updateSearchParams = exports.updatePaginationParams = exports.loadPage = undefined;
7 |
8 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
9 |
10 | var _utils = require('./utils');
11 |
12 | var loadPage = exports.loadPage = function loadPage(actionTypes, storeName, callApi, config) {
13 | var fetching = actionTypes.fetching,
14 | success = actionTypes.success,
15 | failure = actionTypes.failure,
16 | removeCache = actionTypes.removeCache,
17 | refreshing = actionTypes.refreshing,
18 | removeSearchResult = actionTypes.removeSearchResult;
19 | var searchHistoryLength = config.searchHistoryLength;
20 |
21 |
22 | return function (page, searchParams) {
23 | return function (dispatch, getState) {
24 |
25 | var store = getState()[storeName];
26 | var entryToRemove = getEntryKeyToRemove(store, searchHistoryLength);
27 | if (entryToRemove) {
28 | dispatch({
29 | type: removeSearchResult,
30 | filtersKey: entryToRemove
31 | });
32 | }
33 |
34 | var validSearchParams = getValidSearchParams(searchParams, store);
35 |
36 | var filtersKey = (0, _utils.buildUniqueKey)(validSearchParams);
37 | var cachedResult = getPage(getState()[storeName], filtersKey, page);
38 | if (shouldAbandonAction(cachedResult, config)) {
39 | return Promise.resolve();
40 | }
41 |
42 | if (!cachedResult) {
43 | dispatch({
44 | type: fetching,
45 | filtersKey: filtersKey,
46 | page: page
47 | });
48 | } else {
49 | dispatch({
50 | type: refreshing,
51 | filtersKey: filtersKey,
52 | page: page
53 | });
54 | }
55 |
56 | return callApi(page, validSearchParams, dispatch, getState).then(function (response) {
57 | if (!isResponseValid(response)) {
58 | dispatch({
59 | type: failure,
60 | filtersKey: filtersKey,
61 | page: page
62 | });
63 | return Promise.reject('server response is not valid');
64 | }
65 |
66 | var newTotalCount = response.totalElements;
67 | var actualTotalCount = undefined;
68 | if (getState()[storeName].pagination[filtersKey]) {
69 | actualTotalCount = getState()[storeName].pagination[filtersKey].elementsCount;
70 | }
71 |
72 | if (actualTotalCount && actualTotalCount !== newTotalCount) {
73 | dispatch({
74 | type: removeCache
75 | });
76 | }
77 | dispatch({
78 | type: success,
79 | filtersKey: filtersKey,
80 | page: page,
81 | response: response
82 | });
83 | }, function (error) {
84 | dispatch({
85 | type: failure,
86 | filtersKey: filtersKey,
87 | page: page
88 | });
89 | return Promise.reject(error);
90 | });
91 | };
92 | };
93 | };
94 |
95 | var getValidSearchParams = function getValidSearchParams(searchParams, store) {
96 | if (!searchParams) {
97 | return store.searchParams;
98 | }
99 | return searchParams;
100 | };
101 |
102 | var getEntryKeyToRemove = function getEntryKeyToRemove(store, searchHistoryLength) {
103 | var paginationEntries = Object.entries(store.pagination);
104 | if (paginationEntries.length > searchHistoryLength) {
105 |
106 | var min = paginationEntries.map(function (_ref) {
107 | var _ref2 = _slicedToArray(_ref, 2),
108 | key = _ref2[0],
109 | val = _ref2[1];
110 |
111 | return { key: key, time: val.lastUpdateTime };
112 | }).reduce(function (min, curr) {
113 | if (!min) {
114 | return curr;
115 | }
116 | if (curr.time < min.time) {
117 | return curr;
118 | }
119 | return min;
120 | }, null);
121 | return min.key;
122 | }
123 | return null;
124 | };
125 |
126 | var shouldAbandonAction = function shouldAbandonAction(cachedResult, config) {
127 | if (!cachedResult) {
128 | return false;
129 | }
130 |
131 | var refreshResultInBackground = config.refreshResultInBackground,
132 | timeToRefresh = config.timeToRefresh;
133 |
134 | if (cachedResult.isFetching || !refreshResultInBackground) {
135 | return true;
136 | }
137 |
138 | if (isTimeToRefresh(cachedResult.lastUpdateTime, timeToRefresh)) {
139 | return false;
140 | }
141 | return true;
142 | };
143 |
144 | var isTimeToRefresh = function isTimeToRefresh(prevTime, diff) {
145 | var result = Date.now() - prevTime > diff;
146 | return result;
147 | };
148 |
149 | var getPage = function getPage(store, filtersKey, page) {
150 | if (store && store.pagination && store.pagination[filtersKey] && store.pagination[filtersKey].pages) {
151 | var foundPage = store.pagination[filtersKey].pages[page];
152 | return foundPage;
153 | }
154 | return null;
155 | };
156 |
157 | var isResponseValid = function isResponseValid(response) {
158 | var totalElements = response.totalElements,
159 | elements = response.elements,
160 | entities = response.entities;
161 |
162 |
163 | if (Number.isInteger(totalElements) && totalElements === 0) {
164 | return true;
165 | }
166 |
167 | return totalElements && elements && entities;
168 | };
169 |
170 | var updatePaginationParams = exports.updatePaginationParams = function updatePaginationParams(_updatePaginationParams) {
171 | return function (paramsToStore) {
172 | return function (dispatch, getState) {
173 | dispatch({
174 | type: _updatePaginationParams,
175 | paginationParams: paramsToStore
176 | });
177 | };
178 | };
179 | };
180 |
181 | var updateSearchParams = exports.updateSearchParams = function updateSearchParams(updateSearchParamsActionType) {
182 | return function (searchParamsToStore) {
183 | return function (dispatch, getState) {
184 | dispatch({
185 | type: updateSearchParamsActionType,
186 | searchParams: searchParamsToStore
187 | });
188 | };
189 | };
190 | };
191 |
192 | var setCurrentPage = exports.setCurrentPage = function setCurrentPage(setCurrentPageNumberActionType) {
193 | return function (currentPageNumber) {
194 | return function (dispatch, getState) {
195 | dispatch({
196 | type: setCurrentPageNumberActionType,
197 | pageNumber: currentPageNumber
198 | });
199 | };
200 | };
201 | };
202 | //# sourceMappingURL=paginationActions.js.map
--------------------------------------------------------------------------------
/dist/VirtualizedListExtensions/VirtualList.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/pagination-lib/VirtualizedListExtensions/VirtualList.js"],"names":["AutoSizerContainer","div","VirtualListWithoutOutline","mapStateToProps","state","props","totalElements","paginator","selectors","getTotalElements","notZeroRowCount","paginationParams","getPaginationParams","searchParams","getSearchParams","mapDispatchToProps","dispatch","updatePaginationParams","VirtualList","isRowLoaded","bind","noRowsRenderer","loadMoreRows","rowRenderer","onRowClick","scrollToIndex","prevSearchParamsKey","startIndex","stopIndex","index","selectedIndex","force","key","style","nextProps","setState","searchParamsKey","scrollToIndexZeroBased","width","height","onRowsRendered","registerChild","rowHeight","propTypes","func","isRequired","number","object"],"mappings":";;;;;;;;;;;AAAA;;;;AACA;;AACA;;AACA;;;;AACA;;AACA;;AACA;;;;;;;;;;;;;;AAEA,IAAMA,qBAAqB,2BAAOC,GAA5B,iBAAN;;AAIA,IAAMC,4BAA4B,uDAA5B,kBAAN;;AAIA,IAAMC,kBAAkB,SAAlBA,eAAkB,CAACC,KAAD,EAAQC,KAAR,EAAkB;AACtC,QAAMC,gBAAgBD,MAAME,SAAN,CAAgBC,SAAhB,CAA0BC,gBAA1B,CAA2CL,KAA3C,CAAtB;AACA,QAAMM,kBAAkBJ,gBAAgB,CAAhB,GAAoBA,aAApB,GAAoC,CAA5D;AACA,WAAO;AACHK,0BAAkBN,MAAME,SAAN,CAAgBC,SAAhB,CAA0BI,mBAA1B,CAA8CR,KAA9C,CADf;AAEHS,sBAAcR,MAAME,SAAN,CAAgBC,SAAhB,CAA0BM,eAA1B,CAA0CV,KAA1C,CAFX;AAGHM;AAHG,KAAP;AAKH,CARD;;AAUA,IAAMK,qBAAqB,SAArBA,kBAAqB,CAACC,QAAD,EAAWX,KAAX,EAAqB;AAC5C,WAAO,+BAAmB;AACtBY,gCAAwBZ,MAAME,SAAN,CAAgBU;AADlB,KAAnB,EAEJD,QAFI,CAAP;AAGH,CAJD;;IAMME,W;;;AAEF,yBAAYb,KAAZ,EAAmB;AAAA;;AAAA,8HACTA,KADS;;AAEf,cAAKc,WAAL,GAAmB,MAAKA,WAAL,CAAiBC,IAAjB,OAAnB;AACA,cAAKC,cAAL,GAAsB,MAAKA,cAAL,CAAoBD,IAApB,OAAtB;AACA,cAAKE,YAAL,GAAoB,MAAKA,YAAL,CAAkBF,IAAlB,OAApB;AACA,cAAKG,WAAL,GAAmB,MAAKA,WAAL,CAAiBH,IAAjB,OAAnB;AACA,cAAKI,UAAL,GAAkB,MAAKA,UAAL,CAAgBJ,IAAhB,OAAlB;AACA,cAAKhB,KAAL,GAAa,EAAEqB,eAAe,CAAjB,EAAb;AACA,cAAKC,mBAAL,GAA2B,2BAAerB,MAAMQ,YAArB,CAA3B;AARe;AASlB;;;;2CAEuC;AACpC;;AADoC,gBAAzBc,UAAyB,QAAzBA,UAAyB;AAAA,gBAAbC,SAAa,QAAbA,SAAa;AAEvC;;;2CAEsB;AAAA,gBAATC,KAAS,SAATA,KAAS;;AACnB,mBAAO,KAAP;AACH;;;yCAEgB;AACb,mBAAQ;AAAA;AAAA;AAAA;AAAA,aAAR;AACH;;;mCAEUA,K,EAAO;AACd,iBAAKxB,KAAL,CAAWY,sBAAX,CAAkC,EAAEa,eAAeD,KAAjB,EAAwBE,OAAO,KAA/B,EAAlC;AACH;;;2CAEkC;AAAA;;AAAA,gBAArBF,KAAqB,SAArBA,KAAqB;AAAA,gBAAdG,GAAc,SAAdA,GAAc;AAAA,gBAATC,KAAS,SAATA,KAAS;;AAC/B,mBACI;AAAA;AAAA,kBAAK,KAAKD,GAAV,EAAe,OAAOC,KAAtB,EAA6B,SAAS,mBAAM;AAAE,+BAAKT,UAAL,CAAgBK,KAAhB;AAAwB,qBAAtE;AACK,qBAAKxB,KAAL,CAAWkB,WAAX,CAAuBM,KAAvB;AADL,aADJ;AAIH;;;kDAEyBK,S,EAAW;AAAA,wCACAA,UAAUvB,gBADV;AAAA,gBACzBmB,aADyB,yBACzBA,aADyB;AAAA,gBACVC,KADU,yBACVA,KADU;;AAEjC,gBAAIA,KAAJ,EAAW;AACP,qBAAKI,QAAL,CAAc,EAAEV,eAAeK,aAAjB,EAAd;AACH;;AAED,gBAAMM,kBAAkB,2BAAeF,UAAUrB,YAAzB,CAAxB;AACA,gBAAIuB,oBAAoB,KAAKV,mBAA7B,EAAkD;AAC9C,qBAAKrB,KAAL,CAAWY,sBAAX,CAAkC,EAAEa,eAAe,IAAjB,EAAuBC,OAAO,IAA9B,EAAlC;AACA,qBAAKL,mBAAL,GAA2BU,eAA3B;AACH;AACJ;;;6CAEoB;AACjB,iBAAKD,QAAL,CAAc,EAAEV,eAAe,KAAKpB,KAAL,CAAWM,gBAAX,CAA4BmB,aAA7C,EAAd;AACH;;;iCAEQ;AAAA;;AACL,gBAAMO,yBAAyB,KAAKjC,KAAL,CAAWqB,aAA1C;AACA,mBACI;AAAC,kCAAD;AAAA;AACI;AAAA;AAAA;AACK;AAAA,4BAAGa,KAAH,SAAGA,KAAH;AAAA,4BAAUC,MAAV,SAAUA,MAAV;AAAA,+BACG;AAAA;AAAA;AACI,qCAAK,2BAAe,OAAKlC,KAAL,CAAWQ,YAA1B,CADT;AAEI,6CAAa,OAAKM,WAFtB;AAGI,8CAAc,OAAKG,YAHvB;AAII,0CAAU,OAAKjB,KAAL,CAAWK,eAJzB;AAKK,6CAAuC;AAAA,oCAApC8B,cAAoC,SAApCA,cAAoC;AAAA,oCAApBC,aAAoB,SAApBA,aAAoB;;AACpC,uCACI,8BAAC,yBAAD;AACI,yCAAKA,aADT;AAEI,4CAAQF,MAFZ;AAGI,2CAAOD,KAHX;AAII,oDAAgBE,cAJpB;AAKI,8CAAU,OAAKnC,KAAL,CAAWK,eALzB;AAMI,iDAAa,OAAKa,WANtB;AAOI,oDAAgB,OAAKF,cAPzB;AAQI,+CAAW,OAAKhB,KAAL,CAAWqC,SAR1B;AASI,mDAAeL,sBATnB;AAUI,uDAAkB;AAVtB,kCADJ;AAaH;AAnBL,yBADH;AAAA;AADL;AADJ,aADJ;AA8BH;;;;;;AAGLnB,YAAYyB,SAAZ,GAAwB;AACpBpB,iBAAa,oBAAUqB,IAAV,CAAeC,UADR;AAEpBH,eAAW,oBAAUI,MAAV,CAAiBD,UAFR;AAGpBtC,eAAW,oBAAUwC,MAAV,CAAiBF;AAHR,CAAxB;;kBAMe,yBAAQ1C,eAAR,EAAyBY,kBAAzB,EAA6CG,WAA7C,C","file":"VirtualList.js","sourcesContent":["import React, { Component } from 'react';\r\nimport { List, InfiniteLoader, AutoSizer } from 'react-virtualized';\r\nimport { connect } from 'react-redux';\r\nimport PropTypes from 'prop-types';\r\nimport { bindActionCreators } from 'redux';\r\nimport { buildUniqueKey } from './../utils';\r\nimport styled from 'styled-components';\r\n\r\nconst AutoSizerContainer = styled.div`\r\n flex: 1 1 auto;\r\n`;\r\n\r\nconst VirtualListWithoutOutline = styled(List)`\r\n outline: none;\r\n`;\r\n\r\nconst mapStateToProps = (state, props) => {\r\n const totalElements = props.paginator.selectors.getTotalElements(state);\r\n const notZeroRowCount = totalElements > 0 ? totalElements : 1;\r\n return {\r\n paginationParams: props.paginator.selectors.getPaginationParams(state),\r\n searchParams: props.paginator.selectors.getSearchParams(state),\r\n notZeroRowCount\r\n }\r\n}\r\n\r\nconst mapDispatchToProps = (dispatch, props) => {\r\n return bindActionCreators({\r\n updatePaginationParams: props.paginator.updatePaginationParams\r\n }, dispatch);\r\n}\r\n\r\nclass VirtualList extends Component {\r\n\r\n constructor(props) {\r\n super(props);\r\n this.isRowLoaded = this.isRowLoaded.bind(this);\r\n this.noRowsRenderer = this.noRowsRenderer.bind(this);\r\n this.loadMoreRows = this.loadMoreRows.bind(this);\r\n this.rowRenderer = this.rowRenderer.bind(this);\r\n this.onRowClick = this.onRowClick.bind(this);\r\n this.state = { scrollToIndex: 0 }\r\n this.prevSearchParamsKey = buildUniqueKey(props.searchParams);\r\n }\r\n\r\n loadMoreRows({ startIndex, stopIndex }) {\r\n //nop\r\n }\r\n\r\n isRowLoaded({ index }) {\r\n return false;\r\n }\r\n\r\n noRowsRenderer() {\r\n return (no rows
);\r\n }\r\n\r\n onRowClick(index) {\r\n this.props.updatePaginationParams({ selectedIndex: index, force: false });\r\n }\r\n\r\n rowRenderer({ index, key, style }) {\r\n return (\r\n { this.onRowClick(index) }}>\r\n {this.props.rowRenderer(index)}\r\n
);\r\n }\r\n\r\n componentWillReceiveProps(nextProps) {\r\n const { selectedIndex, force } = nextProps.paginationParams;\r\n if (force) {\r\n this.setState({ scrollToIndex: selectedIndex });\r\n }\r\n\r\n const searchParamsKey = buildUniqueKey(nextProps.searchParams);\r\n if (searchParamsKey !== this.prevSearchParamsKey) {\r\n this.props.updatePaginationParams({ selectedIndex: null, force: true });\r\n this.prevSearchParamsKey = searchParamsKey;\r\n }\r\n }\r\n\r\n componentWillMount() {\r\n this.setState({ scrollToIndex: this.props.paginationParams.selectedIndex });\r\n }\r\n\r\n render() {\r\n const scrollToIndexZeroBased = this.state.scrollToIndex;\r\n return (\r\n \r\n \r\n {({ width, height }) => (\r\n \r\n {({ onRowsRendered, registerChild }) => {\r\n return (\r\n );\r\n }\r\n }\r\n \r\n )}\r\n \r\n \r\n );\r\n }\r\n}\r\n\r\nVirtualList.propTypes = {\r\n rowRenderer: PropTypes.func.isRequired,\r\n rowHeight: PropTypes.number.isRequired,\r\n paginator: PropTypes.object.isRequired\r\n};\r\n\r\nexport default connect(mapStateToProps, mapDispatchToProps)(VirtualList);\r\n"]}
--------------------------------------------------------------------------------
/dist/VirtualizedListWrapper/VirtualList.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/pagination-lib/VirtualizedListWrapper/VirtualList.js"],"names":["AutoSizerContainer","div","VirtualListWithoutOutline","mapStateToProps","state","props","totalElements","paginator","selectors","getTotalElements","notZeroRowCount","paginationParams","getPaginationParams","searchParams","getSearchParams","mapDispatchToProps","dispatch","updatePaginationParams","requestPage","VirtualList","isRowLoaded","bind","noRowsRenderer","loadMoreRows","rowRenderer","onRowClick","scrollToIndex","prevSearchParamsKey","startIndex","stopIndex","index","selectedIndex","force","key","style","pageNum","convertTotalItemIndexToPageNum","nextProps","setState","searchParamsKey","scrollToIndexZeroBased","width","height","onRowsRendered","registerChild","rowHeight","propTypes","func","isRequired","number","object"],"mappings":"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","file":"VirtualList.js","sourcesContent":["import React, { Component } from 'react';\r\nimport { List, InfiniteLoader, AutoSizer } from 'react-virtualized';\r\nimport { connect } from 'react-redux';\r\nimport PropTypes from 'prop-types';\r\nimport { bindActionCreators } from 'redux';\r\nimport { buildUniqueKey } from './../utils';\r\nimport styled from 'styled-components';\r\n\r\nconst AutoSizerContainer = styled.div`\r\n flex: 1 1 auto;\r\n`;\r\n\r\nconst VirtualListWithoutOutline = styled(List)`\r\n outline: none;\r\n`;\r\n\r\nconst mapStateToProps = (state, props) => {\r\n const totalElements = props.paginator.selectors.getTotalElements(state);\r\n const notZeroRowCount = totalElements > 0 ? totalElements : 1;\r\n return {\r\n paginationParams: props.paginator.selectors.getPaginationParams(state),\r\n searchParams: props.paginator.selectors.getSearchParams(state),\r\n notZeroRowCount\r\n }\r\n}\r\n\r\nconst mapDispatchToProps = (dispatch, props) => {\r\n return bindActionCreators({\r\n updatePaginationParams: props.paginator.updatePaginationParams,\r\n requestPage: props.paginator.requestPage\r\n }, dispatch);\r\n}\r\n\r\nclass VirtualList extends Component {\r\n\r\n constructor(props) {\r\n super(props);\r\n this.isRowLoaded = this.isRowLoaded.bind(this);\r\n this.noRowsRenderer = this.noRowsRenderer.bind(this);\r\n this.loadMoreRows = this.loadMoreRows.bind(this);\r\n this.rowRenderer = this.rowRenderer.bind(this);\r\n this.onRowClick = this.onRowClick.bind(this);\r\n this.state = { scrollToIndex: 0 }\r\n this.prevSearchParamsKey = buildUniqueKey(props.searchParams);\r\n }\r\n\r\n loadMoreRows({ startIndex, stopIndex }) {\r\n //nop\r\n }\r\n\r\n isRowLoaded({ index }) {\r\n return false;\r\n }\r\n\r\n noRowsRenderer() {\r\n return (no rows
);\r\n }\r\n\r\n onRowClick(index) {\r\n this.props.updatePaginationParams({ selectedIndex: index, force: false });\r\n }\r\n\r\n rowRenderer({ index, key, style }) {\r\n const pageNum = this.props.paginator.convertTotalItemIndexToPageNum(index);\r\n this.props.requestPage(pageNum);\r\n return (\r\n { this.onRowClick(index) }}>\r\n {this.props.rowRenderer(index)}\r\n
);\r\n }\r\n\r\n componentWillReceiveProps(nextProps) {\r\n const { selectedIndex, force } = nextProps.paginationParams;\r\n if (force) {\r\n this.setState({ scrollToIndex: selectedIndex });\r\n }\r\n\r\n const searchParamsKey = buildUniqueKey(nextProps.searchParams);\r\n if (searchParamsKey !== this.prevSearchParamsKey) {\r\n this.props.updatePaginationParams({ selectedIndex: null, force: true });\r\n this.prevSearchParamsKey = searchParamsKey;\r\n }\r\n }\r\n\r\n componentWillMount() {\r\n this.setState({ scrollToIndex: this.props.paginationParams.selectedIndex });\r\n }\r\n\r\n render() {\r\n const scrollToIndexZeroBased = this.state.scrollToIndex;\r\n return (\r\n \r\n \r\n {({ width, height }) => (\r\n \r\n {({ onRowsRendered, registerChild }) => {\r\n return (\r\n );\r\n }\r\n }\r\n \r\n )}\r\n \r\n \r\n );\r\n }\r\n}\r\n\r\nVirtualList.propTypes = {\r\n rowRenderer: PropTypes.func.isRequired,\r\n rowHeight: PropTypes.number.isRequired,\r\n paginator: PropTypes.object.isRequired\r\n};\r\n\r\nexport default connect(mapStateToProps, mapDispatchToProps)(VirtualList);\r\n"]}
--------------------------------------------------------------------------------
/dist/VirtualizedListExtensions/VirtualList.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _templateObject = _taggedTemplateLiteral(['\n flex: 1 1 auto;\n'], ['\n flex: 1 1 auto;\n']),
10 | _templateObject2 = _taggedTemplateLiteral(['\n outline: none;\n'], ['\n outline: none;\n']);
11 |
12 | var _react = require('react');
13 |
14 | var _react2 = _interopRequireDefault(_react);
15 |
16 | var _reactVirtualized = require('react-virtualized');
17 |
18 | var _reactRedux = require('react-redux');
19 |
20 | var _propTypes = require('prop-types');
21 |
22 | var _propTypes2 = _interopRequireDefault(_propTypes);
23 |
24 | var _redux = require('redux');
25 |
26 | var _utils = require('./../utils');
27 |
28 | var _styledComponents = require('styled-components');
29 |
30 | var _styledComponents2 = _interopRequireDefault(_styledComponents);
31 |
32 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33 |
34 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
35 |
36 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
37 |
38 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
39 |
40 | function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
41 |
42 | var AutoSizerContainer = _styledComponents2.default.div(_templateObject);
43 |
44 | var VirtualListWithoutOutline = (0, _styledComponents2.default)(_reactVirtualized.List)(_templateObject2);
45 |
46 | var mapStateToProps = function mapStateToProps(state, props) {
47 | var totalElements = props.paginator.selectors.getTotalElements(state);
48 | var notZeroRowCount = totalElements > 0 ? totalElements : 1;
49 | return {
50 | paginationParams: props.paginator.selectors.getPaginationParams(state),
51 | searchParams: props.paginator.selectors.getSearchParams(state),
52 | notZeroRowCount: notZeroRowCount
53 | };
54 | };
55 |
56 | var mapDispatchToProps = function mapDispatchToProps(dispatch, props) {
57 | return (0, _redux.bindActionCreators)({
58 | updatePaginationParams: props.paginator.updatePaginationParams
59 | }, dispatch);
60 | };
61 |
62 | var VirtualList = function (_Component) {
63 | _inherits(VirtualList, _Component);
64 |
65 | function VirtualList(props) {
66 | _classCallCheck(this, VirtualList);
67 |
68 | var _this = _possibleConstructorReturn(this, (VirtualList.__proto__ || Object.getPrototypeOf(VirtualList)).call(this, props));
69 |
70 | _this.isRowLoaded = _this.isRowLoaded.bind(_this);
71 | _this.noRowsRenderer = _this.noRowsRenderer.bind(_this);
72 | _this.loadMoreRows = _this.loadMoreRows.bind(_this);
73 | _this.rowRenderer = _this.rowRenderer.bind(_this);
74 | _this.onRowClick = _this.onRowClick.bind(_this);
75 | _this.state = { scrollToIndex: 0 };
76 | _this.prevSearchParamsKey = (0, _utils.buildUniqueKey)(props.searchParams);
77 | return _this;
78 | }
79 |
80 | _createClass(VirtualList, [{
81 | key: 'loadMoreRows',
82 | value: function loadMoreRows(_ref) {
83 | //nop
84 |
85 | var startIndex = _ref.startIndex,
86 | stopIndex = _ref.stopIndex;
87 | }
88 | }, {
89 | key: 'isRowLoaded',
90 | value: function isRowLoaded(_ref2) {
91 | var index = _ref2.index;
92 |
93 | return false;
94 | }
95 | }, {
96 | key: 'noRowsRenderer',
97 | value: function noRowsRenderer() {
98 | return _react2.default.createElement(
99 | 'div',
100 | null,
101 | 'no rows'
102 | );
103 | }
104 | }, {
105 | key: 'onRowClick',
106 | value: function onRowClick(index) {
107 | this.props.updatePaginationParams({ selectedIndex: index, force: false });
108 | }
109 | }, {
110 | key: 'rowRenderer',
111 | value: function rowRenderer(_ref3) {
112 | var _this2 = this;
113 |
114 | var index = _ref3.index,
115 | key = _ref3.key,
116 | style = _ref3.style;
117 |
118 | return _react2.default.createElement(
119 | 'div',
120 | { key: key, style: style, onClick: function onClick() {
121 | _this2.onRowClick(index);
122 | } },
123 | this.props.rowRenderer(index)
124 | );
125 | }
126 | }, {
127 | key: 'componentWillReceiveProps',
128 | value: function componentWillReceiveProps(nextProps) {
129 | var _nextProps$pagination = nextProps.paginationParams,
130 | selectedIndex = _nextProps$pagination.selectedIndex,
131 | force = _nextProps$pagination.force;
132 |
133 | if (force) {
134 | this.setState({ scrollToIndex: selectedIndex });
135 | }
136 |
137 | var searchParamsKey = (0, _utils.buildUniqueKey)(nextProps.searchParams);
138 | if (searchParamsKey !== this.prevSearchParamsKey) {
139 | this.props.updatePaginationParams({ selectedIndex: null, force: true });
140 | this.prevSearchParamsKey = searchParamsKey;
141 | }
142 | }
143 | }, {
144 | key: 'componentWillMount',
145 | value: function componentWillMount() {
146 | this.setState({ scrollToIndex: this.props.paginationParams.selectedIndex });
147 | }
148 | }, {
149 | key: 'render',
150 | value: function render() {
151 | var _this3 = this;
152 |
153 | var scrollToIndexZeroBased = this.state.scrollToIndex;
154 | return _react2.default.createElement(
155 | AutoSizerContainer,
156 | null,
157 | _react2.default.createElement(
158 | _reactVirtualized.AutoSizer,
159 | null,
160 | function (_ref4) {
161 | var width = _ref4.width,
162 | height = _ref4.height;
163 | return _react2.default.createElement(
164 | _reactVirtualized.InfiniteLoader,
165 | {
166 | key: (0, _utils.buildUniqueKey)(_this3.props.searchParams),
167 | isRowLoaded: _this3.isRowLoaded,
168 | loadMoreRows: _this3.loadMoreRows,
169 | rowCount: _this3.props.notZeroRowCount },
170 | function (_ref5) {
171 | var onRowsRendered = _ref5.onRowsRendered,
172 | registerChild = _ref5.registerChild;
173 |
174 | return _react2.default.createElement(VirtualListWithoutOutline, {
175 | ref: registerChild,
176 | height: height,
177 | width: width,
178 | onRowsRendered: onRowsRendered,
179 | rowCount: _this3.props.notZeroRowCount,
180 | rowRenderer: _this3.rowRenderer,
181 | noRowsRenderer: _this3.noRowsRenderer,
182 | rowHeight: _this3.props.rowHeight,
183 | scrollToIndex: scrollToIndexZeroBased,
184 | scrollToAlignment: 'center'
185 | });
186 | }
187 | );
188 | }
189 | )
190 | );
191 | }
192 | }]);
193 |
194 | return VirtualList;
195 | }(_react.Component);
196 |
197 | VirtualList.propTypes = {
198 | rowRenderer: _propTypes2.default.func.isRequired,
199 | rowHeight: _propTypes2.default.number.isRequired,
200 | paginator: _propTypes2.default.object.isRequired
201 | };
202 |
203 | exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(VirtualList);
204 | //# sourceMappingURL=VirtualList.js.map
--------------------------------------------------------------------------------
/dist/VirtualizedListWrapper/VirtualList.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _templateObject = _taggedTemplateLiteral(['\n flex: 1 1 auto;\n'], ['\n flex: 1 1 auto;\n']),
10 | _templateObject2 = _taggedTemplateLiteral(['\n outline: none;\n'], ['\n outline: none;\n']);
11 |
12 | var _react = require('react');
13 |
14 | var _react2 = _interopRequireDefault(_react);
15 |
16 | var _reactVirtualized = require('react-virtualized');
17 |
18 | var _reactRedux = require('react-redux');
19 |
20 | var _propTypes = require('prop-types');
21 |
22 | var _propTypes2 = _interopRequireDefault(_propTypes);
23 |
24 | var _redux = require('redux');
25 |
26 | var _utils = require('./../utils');
27 |
28 | var _styledComponents = require('styled-components');
29 |
30 | var _styledComponents2 = _interopRequireDefault(_styledComponents);
31 |
32 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33 |
34 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
35 |
36 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
37 |
38 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
39 |
40 | function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
41 |
42 | var AutoSizerContainer = _styledComponents2.default.div(_templateObject);
43 |
44 | var VirtualListWithoutOutline = (0, _styledComponents2.default)(_reactVirtualized.List)(_templateObject2);
45 |
46 | var mapStateToProps = function mapStateToProps(state, props) {
47 | var totalElements = props.paginator.selectors.getTotalElements(state);
48 | var notZeroRowCount = totalElements > 0 ? totalElements : 1;
49 | return {
50 | paginationParams: props.paginator.selectors.getPaginationParams(state),
51 | searchParams: props.paginator.selectors.getSearchParams(state),
52 | notZeroRowCount: notZeroRowCount
53 | };
54 | };
55 |
56 | var mapDispatchToProps = function mapDispatchToProps(dispatch, props) {
57 | return (0, _redux.bindActionCreators)({
58 | updatePaginationParams: props.paginator.updatePaginationParams,
59 | requestPage: props.paginator.requestPage
60 | }, dispatch);
61 | };
62 |
63 | var VirtualList = function (_Component) {
64 | _inherits(VirtualList, _Component);
65 |
66 | function VirtualList(props) {
67 | _classCallCheck(this, VirtualList);
68 |
69 | var _this = _possibleConstructorReturn(this, (VirtualList.__proto__ || Object.getPrototypeOf(VirtualList)).call(this, props));
70 |
71 | _this.isRowLoaded = _this.isRowLoaded.bind(_this);
72 | _this.noRowsRenderer = _this.noRowsRenderer.bind(_this);
73 | _this.loadMoreRows = _this.loadMoreRows.bind(_this);
74 | _this.rowRenderer = _this.rowRenderer.bind(_this);
75 | _this.onRowClick = _this.onRowClick.bind(_this);
76 | _this.state = { scrollToIndex: 0 };
77 | _this.prevSearchParamsKey = (0, _utils.buildUniqueKey)(props.searchParams);
78 | return _this;
79 | }
80 |
81 | _createClass(VirtualList, [{
82 | key: 'loadMoreRows',
83 | value: function loadMoreRows(_ref) {
84 | //nop
85 |
86 | var startIndex = _ref.startIndex,
87 | stopIndex = _ref.stopIndex;
88 | }
89 | }, {
90 | key: 'isRowLoaded',
91 | value: function isRowLoaded(_ref2) {
92 | var index = _ref2.index;
93 |
94 | return false;
95 | }
96 | }, {
97 | key: 'noRowsRenderer',
98 | value: function noRowsRenderer() {
99 | return _react2.default.createElement(
100 | 'div',
101 | null,
102 | 'no rows'
103 | );
104 | }
105 | }, {
106 | key: 'onRowClick',
107 | value: function onRowClick(index) {
108 | this.props.updatePaginationParams({ selectedIndex: index, force: false });
109 | }
110 | }, {
111 | key: 'rowRenderer',
112 | value: function rowRenderer(_ref3) {
113 | var _this2 = this;
114 |
115 | var index = _ref3.index,
116 | key = _ref3.key,
117 | style = _ref3.style;
118 |
119 | var pageNum = this.props.paginator.convertTotalItemIndexToPageNum(index);
120 | this.props.requestPage(pageNum);
121 | return _react2.default.createElement(
122 | 'div',
123 | { key: key, style: style, onClick: function onClick() {
124 | _this2.onRowClick(index);
125 | } },
126 | this.props.rowRenderer(index)
127 | );
128 | }
129 | }, {
130 | key: 'componentWillReceiveProps',
131 | value: function componentWillReceiveProps(nextProps) {
132 | var _nextProps$pagination = nextProps.paginationParams,
133 | selectedIndex = _nextProps$pagination.selectedIndex,
134 | force = _nextProps$pagination.force;
135 |
136 | if (force) {
137 | this.setState({ scrollToIndex: selectedIndex });
138 | }
139 |
140 | var searchParamsKey = (0, _utils.buildUniqueKey)(nextProps.searchParams);
141 | if (searchParamsKey !== this.prevSearchParamsKey) {
142 | this.props.updatePaginationParams({ selectedIndex: null, force: true });
143 | this.prevSearchParamsKey = searchParamsKey;
144 | }
145 | }
146 | }, {
147 | key: 'componentWillMount',
148 | value: function componentWillMount() {
149 | this.setState({ scrollToIndex: this.props.paginationParams.selectedIndex });
150 | }
151 | }, {
152 | key: 'render',
153 | value: function render() {
154 | var _this3 = this;
155 |
156 | var scrollToIndexZeroBased = this.state.scrollToIndex;
157 | return _react2.default.createElement(
158 | AutoSizerContainer,
159 | null,
160 | _react2.default.createElement(
161 | _reactVirtualized.AutoSizer,
162 | null,
163 | function (_ref4) {
164 | var width = _ref4.width,
165 | height = _ref4.height;
166 | return _react2.default.createElement(
167 | _reactVirtualized.InfiniteLoader,
168 | {
169 | key: (0, _utils.buildUniqueKey)(_this3.props.searchParams),
170 | isRowLoaded: _this3.isRowLoaded,
171 | loadMoreRows: _this3.loadMoreRows,
172 | rowCount: _this3.props.notZeroRowCount },
173 | function (_ref5) {
174 | var onRowsRendered = _ref5.onRowsRendered,
175 | registerChild = _ref5.registerChild;
176 |
177 | return _react2.default.createElement(VirtualListWithoutOutline, {
178 | ref: registerChild,
179 | height: height,
180 | width: width,
181 | onRowsRendered: onRowsRendered,
182 | rowCount: _this3.props.notZeroRowCount,
183 | rowRenderer: _this3.rowRenderer,
184 | noRowsRenderer: _this3.noRowsRenderer,
185 | rowHeight: _this3.props.rowHeight,
186 | scrollToIndex: scrollToIndexZeroBased,
187 | scrollToAlignment: 'center'
188 | });
189 | }
190 | );
191 | }
192 | )
193 | );
194 | }
195 | }]);
196 |
197 | return VirtualList;
198 | }(_react.Component);
199 |
200 | VirtualList.propTypes = {
201 | rowRenderer: _propTypes2.default.func.isRequired,
202 | rowHeight: _propTypes2.default.number.isRequired,
203 | paginator: _propTypes2.default.object.isRequired
204 | };
205 |
206 | exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(VirtualList);
207 | //# sourceMappingURL=VirtualList.js.map
--------------------------------------------------------------------------------
/dist/paginationActions.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/pagination-lib/paginationActions.js"],"names":["loadPage","actionTypes","storeName","callApi","config","fetching","success","failure","removeCache","refreshing","removeSearchResult","searchHistoryLength","page","searchParams","dispatch","getState","store","entryToRemove","getEntryKeyToRemove","type","filtersKey","validSearchParams","getValidSearchParams","cachedResult","getPage","shouldAbandonAction","Promise","resolve","then","isResponseValid","response","reject","newTotalCount","totalElements","actualTotalCount","undefined","pagination","elementsCount","error","paginationEntries","Object","entries","length","min","map","key","val","time","lastUpdateTime","reduce","curr","refreshResultInBackground","timeToRefresh","isFetching","isTimeToRefresh","prevTime","diff","result","Date","now","pages","foundPage","elements","entities","Number","isInteger","updatePaginationParams","paramsToStore","paginationParams","updateSearchParams","updateSearchParamsActionType","searchParamsToStore","setCurrentPage","setCurrentPageNumberActionType","currentPageNumber","pageNumber"],"mappings":";;;;;;;;;AACA;;AAEO,IAAMA,8BAAW,SAAXA,QAAW,CAACC,WAAD,EAAcC,SAAd,EAAyBC,OAAzB,EAAkCC,MAAlC,EAA6C;AAAA,QACzDC,QADyD,GAMtCJ,WANsC,CACzDI,QADyD;AAAA,QAE7DC,OAF6D,GAMtCL,WANsC,CAE7DK,OAF6D;AAAA,QAG7DC,OAH6D,GAMtCN,WANsC,CAG7DM,OAH6D;AAAA,QAI7DC,WAJ6D,GAMtCP,WANsC,CAI7DO,WAJ6D;AAAA,QAK7DC,UAL6D,GAMtCR,WANsC,CAK7DQ,UAL6D;AAAA,QAM7DC,kBAN6D,GAMtCT,WANsC,CAM7DS,kBAN6D;AAAA,QAOzDC,mBAPyD,GAOjCP,MAPiC,CAOzDO,mBAPyD;;;AASjE,WAAO,UAACC,IAAD,EAAOC,YAAP;AAAA,eACH,UAACC,QAAD,EAAWC,QAAX,EAAwB;;AAEpB,gBAAMC,QAAQD,WAAWb,SAAX,CAAd;AACA,gBAAMe,gBAAgBC,oBAAoBF,KAApB,EAA2BL,mBAA3B,CAAtB;AACA,gBAAIM,aAAJ,EAAmB;AACfH,yBAAS;AACLK,0BAAMT,kBADD;AAELU,gCAAYH;AAFP,iBAAT;AAIH;;AAED,gBAAMI,oBAAoBC,qBAAqBT,YAArB,EAAmCG,KAAnC,CAA1B;;AAEA,gBAAMI,aAAa,2BAAeC,iBAAf,CAAnB;AACA,gBAAME,eAAeC,QAAQT,WAAWb,SAAX,CAAR,EAA+BkB,UAA/B,EAA2CR,IAA3C,CAArB;AACA,gBAAIa,oBAAoBF,YAApB,EAAkCnB,MAAlC,CAAJ,EAA+C;AAC3C,uBAAOsB,QAAQC,OAAR,EAAP;AACH;;AAED,gBAAI,CAACJ,YAAL,EAAmB;AACfT,yBAAS;AACLK,0BAAMd,QADD;AAELe,gCAAYA,UAFP;AAGLR,0BAAMA;AAHD,iBAAT;AAKH,aAND,MAMO;AACHE,yBAAS;AACLK,0BAAMV,UADD;AAELW,gCAAYA,UAFP;AAGLR,0BAAMA;AAHD,iBAAT;AAKH;;AAED,mBAAOT,QAAQS,IAAR,EAAcS,iBAAd,EAAiCP,QAAjC,EAA2CC,QAA3C,EAAqDa,IAArD,CACH,oBAAY;AACR,oBAAI,CAACC,gBAAgBC,QAAhB,CAAL,EAAgC;AAC5BhB,6BAAS;AACLK,8BAAMZ,OADD;AAELa,oCAAYA,UAFP;AAGLR,8BAAMA;AAHD,qBAAT;AAKA,2BAAOc,QAAQK,MAAR,CAAe,8BAAf,CAAP;AACH;;AAED,oBAAMC,gBAAgBF,SAASG,aAA/B;AACA,oBAAIC,mBAAmBC,SAAvB;AACA,oBAAIpB,WAAWb,SAAX,EAAsBkC,UAAtB,CAAiChB,UAAjC,CAAJ,EAAkD;AAC9Cc,uCAAmBnB,WAAWb,SAAX,EAAsBkC,UAAtB,CAAiChB,UAAjC,EAA6CiB,aAAhE;AACH;;AAED,oBAAIH,oBAAoBA,qBAAqBF,aAA7C,EAA4D;AACxDlB,6BAAS;AACLK,8BAAMX;AADD,qBAAT;AAGH;AACDM,yBAAS;AACLK,0BAAMb,OADD;AAELc,gCAAYA,UAFP;AAGLR,0BAAMA,IAHD;AAILkB,8BAAUA;AAJL,iBAAT;AAMH,aA5BE,EA6BH,iBAAS;AACLhB,yBAAS;AACLK,0BAAMZ,OADD;AAELa,gCAAYA,UAFP;AAGLR,0BAAMA;AAHD,iBAAT;AAKA,uBAAOc,QAAQK,MAAR,CAAeO,KAAf,CAAP;AACH,aApCE,CAAP;AAsCH,SAxEE;AAAA,KAAP;AAyEH,CAlFM;;AAoFP,IAAMhB,uBAAuB,SAAvBA,oBAAuB,CAACT,YAAD,EAAeG,KAAf,EAAyB;AAClD,QAAI,CAACH,YAAL,EAAmB;AACf,eAAOG,MAAMH,YAAb;AACH;AACD,WAAOA,YAAP;AACH,CALD;;AAOA,IAAMK,sBAAsB,SAAtBA,mBAAsB,CAACF,KAAD,EAAQL,mBAAR,EAAgC;AACxD,QAAM4B,oBAAoBC,OAAOC,OAAP,CAAezB,MAAMoB,UAArB,CAA1B;AACA,QAAIG,kBAAkBG,MAAlB,GAA2B/B,mBAA/B,EAAoD;;AAEhD,YAAMgC,MAAMJ,kBAAkBK,GAAlB,CAAsB,gBAAgB;AAAA;AAAA,gBAAdC,GAAc;AAAA,gBAATC,GAAS;;AAC9C,mBAAO,EAAED,KAAKA,GAAP,EAAYE,MAAMD,IAAIE,cAAtB,EAAP;AACH,SAFW,EAETC,MAFS,CAEF,UAACN,GAAD,EAAMO,IAAN,EAAe;AACrB,gBAAI,CAACP,GAAL,EAAU;AACN,uBAAOO,IAAP;AACH;AACD,gBAAIA,KAAKH,IAAL,GAAYJ,IAAII,IAApB,EAA0B;AACtB,uBAAOG,IAAP;AACH;AACD,mBAAOP,GAAP;AACH,SAVW,EAUT,IAVS,CAAZ;AAWA,eAAOA,IAAIE,GAAX;AACH;AACD,WAAO,IAAP;AACH,CAlBD;;AAoBA,IAAMpB,sBAAsB,SAAtBA,mBAAsB,CAACF,YAAD,EAAenB,MAAf,EAA0B;AAClD,QAAI,CAACmB,YAAL,EAAmB;AACf,eAAO,KAAP;AACH;;AAHiD,QAK1C4B,yBAL0C,GAKG/C,MALH,CAK1C+C,yBAL0C;AAAA,QAKfC,aALe,GAKGhD,MALH,CAKfgD,aALe;;AAMlD,QAAI7B,aAAa8B,UAAb,IAA2B,CAACF,yBAAhC,EAA2D;AACvD,eAAO,IAAP;AACH;;AAED,QAAIG,gBAAgB/B,aAAayB,cAA7B,EAA6CI,aAA7C,CAAJ,EAAiE;AAC7D,eAAO,KAAP;AACH;AACD,WAAO,IAAP;AACH,CAdD;;AAgBA,IAAME,kBAAkB,SAAlBA,eAAkB,CAACC,QAAD,EAAWC,IAAX,EAAoB;AACxC,QAAMC,SAASC,KAAKC,GAAL,KAAaJ,QAAb,GAAwBC,IAAvC;AACA,WAAOC,MAAP;AACH,CAHD;;AAKA,IAAMjC,UAAU,SAAVA,OAAU,CAACR,KAAD,EAAQI,UAAR,EAAoBR,IAApB,EAA6B;AACzC,QAAII,SAASA,MAAMoB,UAAf,IAA6BpB,MAAMoB,UAAN,CAAiBhB,UAAjB,CAA7B,IACAJ,MAAMoB,UAAN,CAAiBhB,UAAjB,EAA6BwC,KADjC,EACwC;AACpC,YAAMC,YAAY7C,MAAMoB,UAAN,CAAiBhB,UAAjB,EAA6BwC,KAA7B,CAAmChD,IAAnC,CAAlB;AACA,eAAOiD,SAAP;AACH;AACD,WAAO,IAAP;AACH,CAPD;;AASA,IAAMhC,kBAAkB,SAAlBA,eAAkB,CAACC,QAAD,EAAc;AAAA,QAC1BG,aAD0B,GACYH,QADZ,CAC1BG,aAD0B;AAAA,QACX6B,QADW,GACYhC,QADZ,CACXgC,QADW;AAAA,QACDC,QADC,GACYjC,QADZ,CACDiC,QADC;;;AAGlC,QAAIC,OAAOC,SAAP,CAAiBhC,aAAjB,KAAmCA,kBAAkB,CAAzD,EAA4D;AACxD,eAAO,IAAP;AACH;;AAED,WAAOA,iBAAiB6B,QAAjB,IAA6BC,QAApC;AACH,CARD;;AAUO,IAAMG,0DAAyB,gCAACA,uBAAD,EAA4B;AAC9D,WAAO,UAACC,aAAD;AAAA,eAAmB,UAACrD,QAAD,EAAWC,QAAX,EAAwB;AAC9CD,qBAAS;AACLK,sBAAM+C,uBADD;AAELE,kCAAkBD;AAFb,aAAT;AAIH,SALM;AAAA,KAAP;AAMH,CAPM;;AASA,IAAME,kDAAqB,SAArBA,kBAAqB,CAACC,4BAAD,EAAkC;AAChE,WAAO,UAACC,mBAAD;AAAA,eAAyB,UAACzD,QAAD,EAAWC,QAAX,EAAwB;AACpDD,qBAAS;AACLK,sBAAMmD,4BADD;AAELzD,8BAAc0D;AAFT,aAAT;AAIH,SALM;AAAA,KAAP;AAMH,CAPM;;AASA,IAAMC,0CAAiB,SAAjBA,cAAiB,CAACC,8BAAD,EAAoC;AAC9D,WAAO,UAACC,iBAAD;AAAA,eAAuB,UAAC5D,QAAD,EAAWC,QAAX,EAAwB;AAClDD,qBAAS;AACLK,sBAAMsD,8BADD;AAELE,4BAAYD;AAFP,aAAT;AAIH,SALM;AAAA,KAAP;AAMH,CAPM","file":"paginationActions.js","sourcesContent":["\r\nimport { buildUniqueKey } from './utils';\r\n\r\nexport const loadPage = (actionTypes, storeName, callApi, config) => {\r\n const { fetching,\r\n success,\r\n failure,\r\n removeCache,\r\n refreshing,\r\n removeSearchResult } = actionTypes;\r\n const { searchHistoryLength } = config;\r\n\r\n return (page, searchParams) =>\r\n (dispatch, getState) => {\r\n\r\n const store = getState()[storeName];\r\n const entryToRemove = getEntryKeyToRemove(store, searchHistoryLength);\r\n if (entryToRemove) {\r\n dispatch({\r\n type: removeSearchResult,\r\n filtersKey: entryToRemove\r\n });\r\n }\r\n\r\n const validSearchParams = getValidSearchParams(searchParams, store);\r\n\r\n const filtersKey = buildUniqueKey(validSearchParams);\r\n const cachedResult = getPage(getState()[storeName], filtersKey, page);\r\n if (shouldAbandonAction(cachedResult, config)) {\r\n return Promise.resolve();\r\n }\r\n\r\n if (!cachedResult) {\r\n dispatch({\r\n type: fetching,\r\n filtersKey: filtersKey,\r\n page: page\r\n });\r\n } else {\r\n dispatch({\r\n type: refreshing,\r\n filtersKey: filtersKey,\r\n page: page\r\n });\r\n }\r\n\r\n return callApi(page, validSearchParams, dispatch, getState).then(\r\n response => {\r\n if (!isResponseValid(response)) {\r\n dispatch({\r\n type: failure,\r\n filtersKey: filtersKey,\r\n page: page\r\n });\r\n return Promise.reject('server response is not valid');\r\n }\r\n\r\n const newTotalCount = response.totalElements;\r\n let actualTotalCount = undefined;\r\n if (getState()[storeName].pagination[filtersKey]) {\r\n actualTotalCount = getState()[storeName].pagination[filtersKey].elementsCount;\r\n }\r\n\r\n if (actualTotalCount && actualTotalCount !== newTotalCount) {\r\n dispatch({\r\n type: removeCache\r\n })\r\n }\r\n dispatch({\r\n type: success,\r\n filtersKey: filtersKey,\r\n page: page,\r\n response: response\r\n });\r\n },\r\n error => {\r\n dispatch({\r\n type: failure,\r\n filtersKey: filtersKey,\r\n page: page\r\n });\r\n return Promise.reject(error);\r\n }\r\n )\r\n }\r\n}\r\n\r\nconst getValidSearchParams = (searchParams, store) => {\r\n if (!searchParams) {\r\n return store.searchParams;\r\n }\r\n return searchParams;\r\n}\r\n\r\nconst getEntryKeyToRemove = (store, searchHistoryLength) => {\r\n const paginationEntries = Object.entries(store.pagination);\r\n if (paginationEntries.length > searchHistoryLength) {\r\n\r\n const min = paginationEntries.map(([key, val]) => {\r\n return { key: key, time: val.lastUpdateTime }\r\n }).reduce((min, curr) => {\r\n if (!min) {\r\n return curr;\r\n }\r\n if (curr.time < min.time) {\r\n return curr;\r\n }\r\n return min;\r\n }, null);\r\n return min.key\r\n }\r\n return null;\r\n}\r\n\r\nconst shouldAbandonAction = (cachedResult, config) => {\r\n if (!cachedResult) {\r\n return false;\r\n }\r\n\r\n const { refreshResultInBackground, timeToRefresh } = config;\r\n if (cachedResult.isFetching || !refreshResultInBackground) {\r\n return true;\r\n }\r\n\r\n if (isTimeToRefresh(cachedResult.lastUpdateTime, timeToRefresh)) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nconst isTimeToRefresh = (prevTime, diff) => {\r\n const result = Date.now() - prevTime > diff;\r\n return result;\r\n}\r\n\r\nconst getPage = (store, filtersKey, page) => {\r\n if (store && store.pagination && store.pagination[filtersKey] &&\r\n store.pagination[filtersKey].pages) {\r\n const foundPage = store.pagination[filtersKey].pages[page];\r\n return foundPage;\r\n }\r\n return null;\r\n}\r\n\r\nconst isResponseValid = (response) => {\r\n const { totalElements, elements, entities } = response;\r\n\r\n if (Number.isInteger(totalElements) && totalElements === 0) {\r\n return true;\r\n }\r\n\r\n return totalElements && elements && entities;\r\n}\r\n\r\nexport const updatePaginationParams = (updatePaginationParams) => {\r\n return (paramsToStore) => (dispatch, getState) => {\r\n dispatch({\r\n type: updatePaginationParams,\r\n paginationParams: paramsToStore\r\n });\r\n }\r\n}\r\n\r\nexport const updateSearchParams = (updateSearchParamsActionType) => {\r\n return (searchParamsToStore) => (dispatch, getState) => {\r\n dispatch({\r\n type: updateSearchParamsActionType,\r\n searchParams: searchParamsToStore\r\n });\r\n }\r\n}\r\n\r\nexport const setCurrentPage = (setCurrentPageNumberActionType) => {\r\n return (currentPageNumber) => (dispatch, getState) => {\r\n dispatch({\r\n type: setCurrentPageNumberActionType,\r\n pageNumber: currentPageNumber\r\n });\r\n }\r\n}\r\n\r\n"]}
--------------------------------------------------------------------------------