State history for pagination working

pull/149/head
Piero Toffanin 2017-04-12 11:52:40 -04:00
rodzic ac5e54b56b
commit 23af449853
7 zmienionych plików z 103 dodań i 48 usunięć

Wyświetl plik

@ -2,6 +2,11 @@ import React from 'react';
import './css/Dashboard.scss';
import ProjectList from './components/ProjectList';
import EditProjectDialog from './components/EditProjectDialog';
import Utils from './classes/Utils';
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
import $ from 'jquery';
class Dashboard extends React.Component {
@ -32,7 +37,20 @@ class Dashboard extends React.Component {
}
render() {
const projectList = ({ location, history }) => {
let q = Utils.queryParams(location),
page = parseInt(q.page);
return <ProjectList
source={`/api/projects/?ordering=-created_at&page=${page}`}
ref={(domNode) => { this.projectList = domNode; }}
currentPage={page}
history={history}
/>;
};
return (
<Router basename="/dashboard">
<div>
<div className="text-right add-button">
<button type="button"
@ -47,10 +65,9 @@ class Dashboard extends React.Component {
saveAction={this.addNewProject}
ref={(domNode) => { this.projectDialog = domNode; }}
/>
<ProjectList
source="/api/projects/?ordering=-created_at&page=#{PAGE}"
ref={(domNode) => { this.projectList = domNode; }} />
<Route path="/" component={projectList} />
</div>
</Router>
);
}
}

Wyświetl plik

@ -0,0 +1,17 @@
import Utils from './Utils';
class HistoryNav{
constructor(history){
this.history = history;
}
changeQueryString(param, value){
this.history.replace(
this.history.location.pathname +
Utils.replaceSearchQueryParam(this.history.location, param, value) +
this.history.location.hash);
}
}
export default HistoryNav;

Wyświetl plik

@ -25,5 +25,30 @@ export default {
return result;
},
queryParams: function(location){
let params = {};
let paramsRaw = (location.search.replace("?", "").match(/([^&=]+)=?([^&]*)/g) || []);
for (let i in paramsRaw){
let parts = paramsRaw[i].split("=");
params[parts[0]] = parts[1];
}
return params;
},
toSearchQuery: function(params){
let parts = [];
for (let k in params){
parts.push(encodeURIComponent(k) + "=" + encodeURIComponent(params[k]));
}
if (parts.length > 0) return "?" + parts.join("&");
else return "";
},
replaceSearchQueryParam: function(location, param, value){
let q = this.queryParams(location);
q[param] = value;
return this.toSearchQuery(q);
}
};

Wyświetl plik

@ -1,27 +1,33 @@
import React from 'react';
import update from 'immutability-helper';
import HistoryNav from '../classes/HistoryNav';
class Paginated extends React.Component{
constructor(){
super();
this.handlePageChange = this.handlePageChange.bind(this);
static defaultProps = {
currentPage: 1
};
static propTypes = {
history: React.PropTypes.object.isRequired, // reference to the history object coming from the route this component is bound to
currentPage: React.PropTypes.number
};
constructor(props){
super(props);
this.historyNav = new HistoryNav(props.history);
}
updatePagination(itemsPerPage, totalItems){
let currentPage = 1;
let currentPage = this.props.currentPage;
const totalPages = this.totalPages(itemsPerPage, totalItems);
if (this.state.pagination && this.state.pagination.currentPage !== undefined){
currentPage = this.state.pagination.currentPage;
}
if (currentPage > totalPages) currentPage = totalPages;
this.setState({pagination: {
switchingPages: false,
switchingPages: false,
itemsPerPage: itemsPerPage,
totalItems: totalItems,
currentPage: currentPage
totalItems: totalItems
}
});
}
@ -30,14 +36,6 @@ class Paginated extends React.Component{
return Math.ceil(totalItems / itemsPerPage);
}
getPaginatedUrl(base){
const page = this.state.pagination && this.state.pagination.currentPage !== undefined
? this.state.pagination.currentPage
: 1;
return base.replace(/#\{PAGE\}/g, page);
}
setPaginationState(props, done){
this.setState(update(this.state, {
pagination: {
@ -46,29 +44,17 @@ class Paginated extends React.Component{
}), done);
}
handlePageChange(pageNum){
return (e) => {
// Update current page, once rendering is completed, raise
// on page changed event
this.setPaginationState({
currentPage: pageNum,
switchingPages: true
}, () => {
if (this.onPageChanged) this.onPageChanged(pageNum);
});
}
}
handlePageItemsNumChange(delta, needsRefreshCallback){
let currentPage = this.props.currentPage;
const pagesBefore = this.totalPages(this.state.pagination.itemsPerPage, this.state.pagination.totalItems),
pagesAfter = this.totalPages(this.state.pagination.itemsPerPage, this.state.pagination.totalItems + delta);
let currentPage = this.state.pagination.currentPage;
if (currentPage > pagesAfter) currentPage = pagesAfter;
this.historyNav.changeQueryString('page', currentPage);
this.setPaginationState({
totalItems: this.state.pagination.totalItems + delta,
currentPage: currentPage
totalItems: this.state.pagination.totalItems + delta
}, () => {
if (pagesBefore !== pagesAfter) needsRefreshCallback(pagesAfter);
});

Wyświetl plik

@ -1,4 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import $ from 'jquery';
class Paginator extends React.Component {
@ -14,20 +15,20 @@ class Paginator extends React.Component {
<div className={this.props.className}>
<ul className="pagination pagination-sm">
<li className={currentPage === 1 ? "disabled" : ""}>
<a href="javascript:void(0);" onClick={this.props.handlePageChange(1)}>
<Link to={{search: "?page=1"}}>
<span>&laquo;</span>
</a>
</Link>
</li>
{pages.map(page => {
return (<li
key={page + 1}
className={currentPage === (page + 1) ? "active" : ""}
><a href="javascript:void(0);" onClick={this.props.handlePageChange(page + 1)}>{page + 1}</a></li>);
><Link to={{search: "?page=" + (page + 1)}}>{page + 1}</Link></li>);
})}
<li className={currentPage === numPages ? "disabled" : ""}>
<a href="javascript:void(0);" onClick={this.props.handlePageChange(numPages)}>
<Link to={{search: "?page=" + numPages}}>
<span>&raquo;</span>
</a>
</Link>
</li>
</ul>
</div>

Wyświetl plik

@ -6,10 +6,11 @@ import ProjectListItem from './ProjectListItem';
import Paginated from './Paginated';
import Paginator from './Paginator';
import ErrorMessage from './ErrorMessage';
import { Route } from 'react-router-dom';
class ProjectList extends Paginated {
constructor(){
super();
constructor(props){
super(props);
this.state = {
loading: true,
@ -27,12 +28,18 @@ class ProjectList extends Paginated {
this.refresh();
}
componentDidUpdate(prevProps){
if (prevProps.source !== this.props.source){
this.refresh();
}
}
refresh(){
this.setState({refreshing: true});
// Load projects from API
this.serverRequest =
$.getJSON(this.getPaginatedUrl(this.props.source), json => {
$.getJSON(this.props.source, json => {
if (json.results){
this.setState({
projects: json.results,
@ -78,8 +85,8 @@ class ProjectList extends Paginated {
return (<div className="project-list">Loading projects... <i className="fa fa-refresh fa-spin fa-fw"></i></div>);
}else{
return (<div className="project-list">
<ErrorMessage bind={[this, 'error']} />
<Paginator className="text-right" {...this.state.pagination} handlePageChange={this.handlePageChange.bind(this)}>
<ErrorMessage bind={[this, 'error']} />
<Paginator className="text-right" {...this.state.pagination} {...this.props}>
<ul className={"list-group project-list " + (this.state.refreshing ? "refreshing" : "")}>
{this.state.projects.map(p => (
<ProjectListItem key={p.id} data={p} onDelete={this.handleDelete} />

Wyświetl plik

@ -44,6 +44,8 @@
"react": "^15.3.2",
"react-dom": "^15.3.2",
"react-hot-loader": "^3.0.0-beta.5",
"react-router": "^4.1.0",
"react-router-dom": "^4.1.0",
"sass-loader": "^4.0.2",
"style-loader": "^0.13.1",
"tween.js": "^16.6.0",