kopia lustrzana https://github.com/OpenDroneMap/WebODM
State history for pagination working
rodzic
ac5e54b56b
commit
23af449853
|
@ -2,6 +2,11 @@ import React from 'react';
|
||||||
import './css/Dashboard.scss';
|
import './css/Dashboard.scss';
|
||||||
import ProjectList from './components/ProjectList';
|
import ProjectList from './components/ProjectList';
|
||||||
import EditProjectDialog from './components/EditProjectDialog';
|
import EditProjectDialog from './components/EditProjectDialog';
|
||||||
|
import Utils from './classes/Utils';
|
||||||
|
import {
|
||||||
|
BrowserRouter as Router,
|
||||||
|
Route
|
||||||
|
} from 'react-router-dom';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
class Dashboard extends React.Component {
|
class Dashboard extends React.Component {
|
||||||
|
@ -32,7 +37,20 @@ class Dashboard extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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 (
|
return (
|
||||||
|
<Router basename="/dashboard">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-right add-button">
|
<div className="text-right add-button">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
|
@ -47,10 +65,9 @@ class Dashboard extends React.Component {
|
||||||
saveAction={this.addNewProject}
|
saveAction={this.addNewProject}
|
||||||
ref={(domNode) => { this.projectDialog = domNode; }}
|
ref={(domNode) => { this.projectDialog = domNode; }}
|
||||||
/>
|
/>
|
||||||
<ProjectList
|
<Route path="/" component={projectList} />
|
||||||
source="/api/projects/?ordering=-created_at&page=#{PAGE}"
|
|
||||||
ref={(domNode) => { this.projectList = domNode; }} />
|
|
||||||
</div>
|
</div>
|
||||||
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -25,5 +25,30 @@ export default {
|
||||||
|
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,33 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import update from 'immutability-helper';
|
import update from 'immutability-helper';
|
||||||
|
import HistoryNav from '../classes/HistoryNav';
|
||||||
|
|
||||||
class Paginated extends React.Component{
|
class Paginated extends React.Component{
|
||||||
constructor(){
|
static defaultProps = {
|
||||||
super();
|
currentPage: 1
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
};
|
||||||
|
|
||||||
|
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){
|
updatePagination(itemsPerPage, totalItems){
|
||||||
let currentPage = 1;
|
let currentPage = this.props.currentPage;
|
||||||
const totalPages = this.totalPages(itemsPerPage, totalItems);
|
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;
|
if (currentPage > totalPages) currentPage = totalPages;
|
||||||
|
|
||||||
this.setState({pagination: {
|
this.setState({pagination: {
|
||||||
switchingPages: false,
|
switchingPages: false,
|
||||||
itemsPerPage: itemsPerPage,
|
itemsPerPage: itemsPerPage,
|
||||||
totalItems: totalItems,
|
totalItems: totalItems
|
||||||
currentPage: currentPage
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -30,14 +36,6 @@ class Paginated extends React.Component{
|
||||||
return Math.ceil(totalItems / itemsPerPage);
|
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){
|
setPaginationState(props, done){
|
||||||
this.setState(update(this.state, {
|
this.setState(update(this.state, {
|
||||||
pagination: {
|
pagination: {
|
||||||
|
@ -46,29 +44,17 @@ class Paginated extends React.Component{
|
||||||
}), done);
|
}), 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){
|
handlePageItemsNumChange(delta, needsRefreshCallback){
|
||||||
|
let currentPage = this.props.currentPage;
|
||||||
const pagesBefore = this.totalPages(this.state.pagination.itemsPerPage, this.state.pagination.totalItems),
|
const pagesBefore = this.totalPages(this.state.pagination.itemsPerPage, this.state.pagination.totalItems),
|
||||||
pagesAfter = this.totalPages(this.state.pagination.itemsPerPage, this.state.pagination.totalItems + delta);
|
pagesAfter = this.totalPages(this.state.pagination.itemsPerPage, this.state.pagination.totalItems + delta);
|
||||||
let currentPage = this.state.pagination.currentPage;
|
|
||||||
|
|
||||||
if (currentPage > pagesAfter) currentPage = pagesAfter;
|
if (currentPage > pagesAfter) currentPage = pagesAfter;
|
||||||
|
|
||||||
|
this.historyNav.changeQueryString('page', currentPage);
|
||||||
|
|
||||||
this.setPaginationState({
|
this.setPaginationState({
|
||||||
totalItems: this.state.pagination.totalItems + delta,
|
totalItems: this.state.pagination.totalItems + delta
|
||||||
currentPage: currentPage
|
|
||||||
}, () => {
|
}, () => {
|
||||||
if (pagesBefore !== pagesAfter) needsRefreshCallback(pagesAfter);
|
if (pagesBefore !== pagesAfter) needsRefreshCallback(pagesAfter);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
class Paginator extends React.Component {
|
class Paginator extends React.Component {
|
||||||
|
@ -14,20 +15,20 @@ class Paginator extends React.Component {
|
||||||
<div className={this.props.className}>
|
<div className={this.props.className}>
|
||||||
<ul className="pagination pagination-sm">
|
<ul className="pagination pagination-sm">
|
||||||
<li className={currentPage === 1 ? "disabled" : ""}>
|
<li className={currentPage === 1 ? "disabled" : ""}>
|
||||||
<a href="javascript:void(0);" onClick={this.props.handlePageChange(1)}>
|
<Link to={{search: "?page=1"}}>
|
||||||
<span>«</span>
|
<span>«</span>
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{pages.map(page => {
|
{pages.map(page => {
|
||||||
return (<li
|
return (<li
|
||||||
key={page + 1}
|
key={page + 1}
|
||||||
className={currentPage === (page + 1) ? "active" : ""}
|
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" : ""}>
|
<li className={currentPage === numPages ? "disabled" : ""}>
|
||||||
<a href="javascript:void(0);" onClick={this.props.handlePageChange(numPages)}>
|
<Link to={{search: "?page=" + numPages}}>
|
||||||
<span>»</span>
|
<span>»</span>
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,10 +6,11 @@ import ProjectListItem from './ProjectListItem';
|
||||||
import Paginated from './Paginated';
|
import Paginated from './Paginated';
|
||||||
import Paginator from './Paginator';
|
import Paginator from './Paginator';
|
||||||
import ErrorMessage from './ErrorMessage';
|
import ErrorMessage from './ErrorMessage';
|
||||||
|
import { Route } from 'react-router-dom';
|
||||||
|
|
||||||
class ProjectList extends Paginated {
|
class ProjectList extends Paginated {
|
||||||
constructor(){
|
constructor(props){
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
@ -27,12 +28,18 @@ class ProjectList extends Paginated {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps){
|
||||||
|
if (prevProps.source !== this.props.source){
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
refresh(){
|
refresh(){
|
||||||
this.setState({refreshing: true});
|
this.setState({refreshing: true});
|
||||||
|
|
||||||
// Load projects from API
|
// Load projects from API
|
||||||
this.serverRequest =
|
this.serverRequest =
|
||||||
$.getJSON(this.getPaginatedUrl(this.props.source), json => {
|
$.getJSON(this.props.source, json => {
|
||||||
if (json.results){
|
if (json.results){
|
||||||
this.setState({
|
this.setState({
|
||||||
projects: json.results,
|
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>);
|
return (<div className="project-list">Loading projects... <i className="fa fa-refresh fa-spin fa-fw"></i></div>);
|
||||||
}else{
|
}else{
|
||||||
return (<div className="project-list">
|
return (<div className="project-list">
|
||||||
<ErrorMessage bind={[this, 'error']} />
|
<ErrorMessage bind={[this, 'error']} />
|
||||||
<Paginator className="text-right" {...this.state.pagination} handlePageChange={this.handlePageChange.bind(this)}>
|
<Paginator className="text-right" {...this.state.pagination} {...this.props}>
|
||||||
<ul className={"list-group project-list " + (this.state.refreshing ? "refreshing" : "")}>
|
<ul className={"list-group project-list " + (this.state.refreshing ? "refreshing" : "")}>
|
||||||
{this.state.projects.map(p => (
|
{this.state.projects.map(p => (
|
||||||
<ProjectListItem key={p.id} data={p} onDelete={this.handleDelete} />
|
<ProjectListItem key={p.id} data={p} onDelete={this.handleDelete} />
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
"react": "^15.3.2",
|
"react": "^15.3.2",
|
||||||
"react-dom": "^15.3.2",
|
"react-dom": "^15.3.2",
|
||||||
"react-hot-loader": "^3.0.0-beta.5",
|
"react-hot-loader": "^3.0.0-beta.5",
|
||||||
|
"react-router": "^4.1.0",
|
||||||
|
"react-router-dom": "^4.1.0",
|
||||||
"sass-loader": "^4.0.2",
|
"sass-loader": "^4.0.2",
|
||||||
"style-loader": "^0.13.1",
|
"style-loader": "^0.13.1",
|
||||||
"tween.js": "^16.6.0",
|
"tween.js": "^16.6.0",
|
||||||
|
|
Ładowanie…
Reference in New Issue