kopia lustrzana https://github.com/OpenDroneMap/WebODM
Pagination working
rodzic
511f5b8680
commit
ec74ab6def
|
@ -48,7 +48,7 @@ class Dashboard extends React.Component {
|
||||||
ref={(domNode) => { this.projectDialog = domNode; }}
|
ref={(domNode) => { this.projectDialog = domNode; }}
|
||||||
/>
|
/>
|
||||||
<ProjectList
|
<ProjectList
|
||||||
source="/api/projects/?ordering=-created_at&page=1"
|
source="/api/projects/?ordering=-created_at&page=#{PAGE}"
|
||||||
ref={(domNode) => { this.projectList = domNode; }} />
|
ref={(domNode) => { this.projectList = domNode; }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -60,7 +60,8 @@ class EditProjectDialog extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(){
|
componentWillUnmount(){
|
||||||
$(this.modal).off('hidden.bs.modal hidden.bs.modal');
|
$(this.modal).off('hidden.bs.modal hidden.bs.modal')
|
||||||
|
.modal('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(){
|
componentDidUpdate(){
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import '../css/Map.scss';
|
import '../css/Map.scss';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import Leaflet from 'leaflet';
|
import Leaflet from 'leaflet';
|
||||||
import async from 'async/dist/async';
|
import async from 'async';
|
||||||
import 'leaflet-measure/dist/leaflet-measure.css';
|
import 'leaflet-measure/dist/leaflet-measure.css';
|
||||||
import 'leaflet-measure/dist/leaflet-measure';
|
import 'leaflet-measure/dist/leaflet-measure';
|
||||||
import '../vendor/leaflet/L.Control.MousePosition.css';
|
import '../vendor/leaflet/L.Control.MousePosition.css';
|
||||||
|
|
|
@ -1,28 +1,80 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import update from 'react-addons-update';
|
||||||
|
|
||||||
/*abstract*/ class Paginated extends React.Component{
|
/*abstract*/ class Paginated extends React.Component{
|
||||||
constructor(){
|
constructor(){
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPagination(itemsPerPage, totalItems){
|
updatePagination(itemsPerPage, totalItems){
|
||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
|
const totalPages = this.totalPages(itemsPerPage, totalItems);
|
||||||
|
|
||||||
if (this.state.pagination && this.state.pagination.currentPage !== undefined){
|
if (this.state.pagination && this.state.pagination.currentPage !== undefined){
|
||||||
currentPage = this.state.pagination.currentPage;
|
currentPage = this.state.pagination.currentPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentPage > totalPages) currentPage = totalPages;
|
||||||
|
|
||||||
this.setState({pagination: {
|
this.setState({pagination: {
|
||||||
itemsPerPage: itemsPerPage,
|
switchingPages: false,
|
||||||
totalItems: totalItems,
|
itemsPerPage: itemsPerPage,
|
||||||
currentPage: currentPage
|
totalItems: totalItems,
|
||||||
|
currentPage: currentPage
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPaginatedUrl(base, page){
|
totalPages(itemsPerPage, totalItems){
|
||||||
|
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);
|
return base.replace(/#\{PAGE\}/g, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPaginationState(props, done){
|
||||||
|
this.setState(update(this.state, {
|
||||||
|
pagination: {
|
||||||
|
$merge: props
|
||||||
|
}
|
||||||
|
}), 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){
|
||||||
|
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.setPaginationState({
|
||||||
|
totalItems: this.state.pagination.totalItems + delta,
|
||||||
|
currentPage: currentPage
|
||||||
|
}, () => {
|
||||||
|
if (pagesBefore !== pagesAfter) needsRefreshCallback(pagesAfter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
throw new Error("Override me");
|
throw new Error("Override me");
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ 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="#">
|
<a href="javascript:void(0);" onClick={this.props.handlePageChange(1)}>
|
||||||
<span>«</span>
|
<span>«</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -22,10 +22,10 @@ class Paginator extends React.Component {
|
||||||
return (<li
|
return (<li
|
||||||
key={page + 1}
|
key={page + 1}
|
||||||
className={currentPage === (page + 1) ? "active" : ""}
|
className={currentPage === (page + 1) ? "active" : ""}
|
||||||
><a href="#">{page + 1}</a></li>);
|
><a href="javascript:void(0);" onClick={this.props.handlePageChange(page + 1)}>{page + 1}</a></li>);
|
||||||
})}
|
})}
|
||||||
<li className={currentPage === numPages ? "disabled" : ""}>
|
<li className={currentPage === numPages ? "disabled" : ""}>
|
||||||
<a href="#">
|
<a href="javascript:void(0);" onClick={this.props.handlePageChange(numPages)}>
|
||||||
<span>»</span>
|
<span>»</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
import '../css/ProjectList.scss';
|
||||||
|
|
||||||
import ProjectListItem from './ProjectListItem';
|
import ProjectListItem from './ProjectListItem';
|
||||||
import Paginated from './Paginated';
|
import Paginated from './Paginated';
|
||||||
import Paginator from './Paginator';
|
import Paginator from './Paginator';
|
||||||
|
import ErrorMessage from './ErrorMessage';
|
||||||
|
|
||||||
class ProjectList extends Paginated {
|
class ProjectList extends Paginated {
|
||||||
constructor(){
|
constructor(){
|
||||||
|
@ -11,10 +13,13 @@ class ProjectList extends Paginated {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
refreshing: false,
|
||||||
error: "",
|
error: "",
|
||||||
projects: null
|
projects: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.PROJECTS_PER_PAGE = 10;
|
||||||
|
|
||||||
this.handleDelete = this.handleDelete.bind(this);
|
this.handleDelete = this.handleDelete.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,15 +28,17 @@ class ProjectList extends Paginated {
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(){
|
refresh(){
|
||||||
|
this.setState({refreshing: true});
|
||||||
|
|
||||||
// Load projects from API
|
// Load projects from API
|
||||||
this.serverRequest =
|
this.serverRequest =
|
||||||
$.getJSON(this.props.source, json => {
|
$.getJSON(this.getPaginatedUrl(this.props.source), json => {
|
||||||
if (json.results){
|
if (json.results){
|
||||||
this.setState({
|
this.setState({
|
||||||
projects: json.results,
|
projects: json.results,
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
this.setupPagination(10, json.count);
|
this.updatePagination(this.PROJECTS_PER_PAGE, json.count);
|
||||||
}else{
|
}else{
|
||||||
this.setState({
|
this.setState({
|
||||||
error: `Invalid JSON response: ${JSON.stringify(json)}`,
|
error: `Invalid JSON response: ${JSON.stringify(json)}`,
|
||||||
|
@ -44,8 +51,14 @@ class ProjectList extends Paginated {
|
||||||
error: `Could not load projects list: ${textStatus}`,
|
error: `Could not load projects list: ${textStatus}`,
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.always(() => {
|
||||||
|
this.setState({refreshing: false});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageChanged(pageNum){
|
||||||
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(){
|
componentWillUnmount(){
|
||||||
|
@ -55,25 +68,25 @@ class ProjectList extends Paginated {
|
||||||
handleDelete(projectId){
|
handleDelete(projectId){
|
||||||
let projects = this.state.projects.filter(p => p.id !== projectId);
|
let projects = this.state.projects.filter(p => p.id !== projectId);
|
||||||
this.setState({projects: projects});
|
this.setState({projects: projects});
|
||||||
|
this.handlePageItemsNumChange(-1, () => {
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.loading){
|
if (this.state.loading){
|
||||||
return (<div>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 if (this.state.projects){
|
|
||||||
return (
|
|
||||||
<Paginator className="text-right" {...this.state.pagination}>
|
|
||||||
<ul className="list-group">
|
|
||||||
{this.state.projects.map(p => (
|
|
||||||
<ProjectListItem key={p.id} data={p} onDelete={this.handleDelete} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</Paginator>);
|
|
||||||
}else if (this.state.error){
|
|
||||||
return (<div>An error occurred: {this.state.error}</div>);
|
|
||||||
}else{
|
}else{
|
||||||
return (<div></div>); // should never happen
|
return (<div className="project-list">
|
||||||
|
<ErrorMessage bind={[this, 'error']} />
|
||||||
|
<Paginator className="text-right" {...this.state.pagination} handlePageChange={this.handlePageChange.bind(this)}>
|
||||||
|
<ul className={"list-group project-list " + (this.state.refreshing ? "refreshing" : "")}>
|
||||||
|
{this.state.projects.map(p => (
|
||||||
|
<ProjectListItem key={p.id} data={p} onDelete={this.handleDelete} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Paginator>
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
.project-list{
|
||||||
|
ul.project-list.refreshing{
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue