Merge pull request #52 from pierotofy/pagination

Pagination
pull/60/head
Piero Toffanin 2016-11-28 15:26:04 -05:00 zatwierdzone przez GitHub
commit 0220691135
12 zmienionych plików z 210 dodań i 21 usunięć

Wyświetl plik

@ -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;

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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>
);

Wyświetl plik

@ -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(){

Wyświetl plik

@ -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';

Wyświetl plik

@ -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;

Wyświetl plik

@ -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>&laquo;</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>&raquo;</span>
</a>
</li>
</ul>
</div>
);
}
return (<div>
{paginator}
{this.props.children}
{paginator}
</div>);
}
}
export default Paginator;

Wyświetl plik

@ -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">
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)}>
<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>);
}else if (this.state.error){
return (<div>An error occurred: {this.state.error}</div>);
}else{
return (<div></div>); // should never happen
</ul>
</Paginator>
</div>);
}
}
}

Wyświetl plik

@ -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';

Wyświetl plik

@ -14,7 +14,7 @@
}
}
.add-button{
.add-button, .list-group{
margin-bottom: 8px;
}
}

Wyświetl plik

@ -0,0 +1,6 @@
.project-list{
ul.project-list.refreshing{
opacity: 0.5;
pointer-events: none;
}
}

Wyświetl plik

@ -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",