kopia lustrzana https://github.com/OpenDroneMap/WebODM
commit
0220691135
|
@ -94,6 +94,40 @@ ul#side-menu.nav{
|
|||
z-index: 999999;
|
||||
}
|
||||
|
||||
.pagination{
|
||||
margin: 0;
|
||||
|
||||
li>a{
|
||||
color: black;
|
||||
border: 1px solid #ecf0f1;
|
||||
background-color: #fff;
|
||||
&:hover, &:focus{
|
||||
background-color: #798d8f;
|
||||
}
|
||||
}
|
||||
.disabled > a{
|
||||
background-color: #fff;
|
||||
border: 1px solid #f4f9f5;
|
||||
&:hover, &:focus{
|
||||
background-color: #fff;
|
||||
border: 1px solid #f4f9f5;
|
||||
}
|
||||
}
|
||||
.active > a{
|
||||
background-color: #798d8f;
|
||||
&:hover, &:focus{
|
||||
background-color: #798d8f;
|
||||
}
|
||||
}
|
||||
|
||||
&.pagination-sm{
|
||||
a{
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.table-first-col-bold{
|
||||
td:first-child{
|
||||
font-weight: bold;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import './css/Console.scss';
|
||||
import './vendor/google-code-prettify/prettify';
|
||||
import './vendor/google-code-prettify/prettify.css';
|
||||
import update from 'react-addons-update';
|
||||
import update from 'immutability-helper';
|
||||
import Utils from './classes/Utils';
|
||||
|
||||
class Console extends React.Component {
|
||||
|
|
|
@ -42,12 +42,13 @@ class Dashboard extends React.Component {
|
|||
Add Project
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<EditProjectDialog
|
||||
saveAction={this.addNewProject}
|
||||
ref={(domNode) => { this.projectDialog = domNode; }}
|
||||
/>
|
||||
<ProjectList
|
||||
source="/api/projects/?ordering=-created_at"
|
||||
source="/api/projects/?ordering=-created_at&page=#{PAGE}"
|
||||
ref={(domNode) => { this.projectList = domNode; }} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -60,7 +60,8 @@ class EditProjectDialog extends React.Component {
|
|||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
$(this.modal).off('hidden.bs.modal hidden.bs.modal');
|
||||
$(this.modal).off('hidden.bs.modal hidden.bs.modal')
|
||||
.modal('hide');
|
||||
}
|
||||
|
||||
componentDidUpdate(){
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import '../css/Map.scss';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
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';
|
||||
import '../vendor/leaflet/L.Control.MousePosition.css';
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import React from 'react';
|
||||
import update from 'immutability-helper';
|
||||
|
||||
class Paginated extends React.Component{
|
||||
constructor(){
|
||||
super();
|
||||
this.handlePageChange = this.handlePageChange.bind(this);
|
||||
}
|
||||
|
||||
updatePagination(itemsPerPage, totalItems){
|
||||
let currentPage = 1;
|
||||
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,
|
||||
itemsPerPage: itemsPerPage,
|
||||
totalItems: totalItems,
|
||||
currentPage: currentPage
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(){
|
||||
throw new Error("Override me");
|
||||
}
|
||||
}
|
||||
|
||||
export default Paginated;
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
import $ from 'jquery';
|
||||
|
||||
class Paginator extends React.Component {
|
||||
render() {
|
||||
const { itemsPerPage, totalItems, currentPage } = this.props;
|
||||
let paginator = null;
|
||||
|
||||
if (itemsPerPage && itemsPerPage && totalItems > itemsPerPage){
|
||||
const numPages = Math.ceil(totalItems / itemsPerPage),
|
||||
pages = [...Array(numPages).keys()]; // [0, 1, 2, ...numPages]
|
||||
|
||||
paginator = (
|
||||
<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)}>
|
||||
<span>«</span>
|
||||
</a>
|
||||
</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>);
|
||||
})}
|
||||
<li className={currentPage === numPages ? "disabled" : ""}>
|
||||
<a href="javascript:void(0);" onClick={this.props.handlePageChange(numPages)}>
|
||||
<span>»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (<div>
|
||||
{paginator}
|
||||
{this.props.children}
|
||||
{paginator}
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export default Paginator;
|
|
@ -1,18 +1,25 @@
|
|||
import React from 'react';
|
||||
import $ from 'jquery';
|
||||
import '../css/ProjectList.scss';
|
||||
|
||||
import ProjectListItem from './ProjectListItem';
|
||||
import Paginated from './Paginated';
|
||||
import Paginator from './Paginator';
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
|
||||
class ProjectList extends React.Component {
|
||||
class ProjectList extends Paginated {
|
||||
constructor(){
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
loading: true,
|
||||
refreshing: false,
|
||||
error: "",
|
||||
projects: null
|
||||
projects: []
|
||||
}
|
||||
|
||||
this.PROJECTS_PER_PAGE = 10;
|
||||
|
||||
this.handleDelete = this.handleDelete.bind(this);
|
||||
}
|
||||
|
||||
|
@ -21,14 +28,17 @@ class ProjectList extends React.Component {
|
|||
}
|
||||
|
||||
refresh(){
|
||||
this.setState({refreshing: true});
|
||||
|
||||
// Load projects from API
|
||||
this.serverRequest =
|
||||
$.getJSON(this.props.source, json => {
|
||||
$.getJSON(this.getPaginatedUrl(this.props.source), json => {
|
||||
if (json.results){
|
||||
this.setState({
|
||||
projects: json.results,
|
||||
loading: false
|
||||
});
|
||||
this.updatePagination(this.PROJECTS_PER_PAGE, json.count);
|
||||
}else{
|
||||
this.setState({
|
||||
error: `Invalid JSON response: ${JSON.stringify(json)}`,
|
||||
|
@ -41,8 +51,14 @@ class ProjectList extends React.Component {
|
|||
error: `Could not load projects list: ${textStatus}`,
|
||||
loading: false
|
||||
});
|
||||
})
|
||||
.always(() => {
|
||||
this.setState({refreshing: false});
|
||||
});
|
||||
}
|
||||
|
||||
onPageChanged(pageNum){
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
|
@ -52,22 +68,25 @@ class ProjectList extends React.Component {
|
|||
handleDelete(projectId){
|
||||
let projects = this.state.projects.filter(p => p.id !== projectId);
|
||||
this.setState({projects: projects});
|
||||
this.handlePageItemsNumChange(-1, () => {
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.loading){
|
||||
return (<div>Loading projects... <i className="fa fa-refresh fa-spin fa-fw"></i></div>);
|
||||
}
|
||||
else if (this.state.projects){
|
||||
return (<ul className="list-group">
|
||||
{this.state.projects.map(p => (
|
||||
<ProjectListItem key={p.id} data={p} onDelete={this.handleDelete} />
|
||||
))}
|
||||
</ul>);
|
||||
}else if (this.state.error){
|
||||
return (<div>An error occurred: {this.state.error}</div>);
|
||||
return (<div className="project-list">Loading projects... <i className="fa fa-refresh fa-spin fa-fw"></i></div>);
|
||||
}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>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import '../css/ProjectListItem.scss';
|
||||
import React from 'react';
|
||||
import update from 'react-addons-update';
|
||||
import update from 'immutability-helper';
|
||||
import TaskList from './TaskList';
|
||||
import EditTaskPanel from './EditTaskPanel';
|
||||
import UploadProgressBar from './UploadProgressBar';
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.add-button{
|
||||
.add-button, .list-group{
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
.project-list{
|
||||
ul.project-list.refreshing{
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
|
@ -30,12 +30,12 @@
|
|||
"css-loader": "^0.25.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.9.0",
|
||||
"immutability-helper": "^2.0.0",
|
||||
"leaflet": "^1.0.1",
|
||||
"leaflet-measure": "^2.0.5",
|
||||
"node-sass": "^3.10.1",
|
||||
"object.values": "^1.0.3",
|
||||
"react": "^15.3.2",
|
||||
"react-addons-update": "^15.3.2",
|
||||
"react-dom": "^15.3.2",
|
||||
"react-hot-loader": "^3.0.0-beta.5",
|
||||
"sass-loader": "^4.0.2",
|
||||
|
|
Ładowanie…
Reference in New Issue