import {Component, EventEmitter, Input, Output, OnChanges, ChangeDetectionStrategy, SimpleChanges} from '@angular/core';
import {getValueInRange, isNumber} from '../util/util';
import {NgbPaginationConfig} from './pagination-config';
/**
* A directive that will take care of visualising a pagination bar and enable / disable buttons correctly!
*/
@Component({
selector: 'ngb-pagination',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {'role': 'navigation'},
template: `
`
})
export class NgbPagination implements OnChanges {
pageCount = 0;
pages: number[] = [];
/**
* Whether to disable buttons from user input
*/
@Input() disabled: boolean;
/**
* Whether to show the "First" and "Last" page links
*/
@Input() boundaryLinks: boolean;
/**
* Whether to show the "Next" and "Previous" page links
*/
@Input() directionLinks: boolean;
/**
* Whether to show ellipsis symbols and first/last page numbers when maxSize > number of pages
*/
@Input() ellipses: boolean;
/**
* Whether to rotate pages when maxSize > number of pages.
* Current page will be in the middle
*/
@Input() rotate: boolean;
/**
* Number of items in collection.
*/
@Input() collectionSize: number;
/**
* Maximum number of pages to display.
*/
@Input() maxSize: number;
/**
* Current page. Page numbers start with 1
*/
@Input() page = 1;
/**
* Number of items per page.
*/
@Input() pageSize: number;
/**
* An event fired when the page is changed.
* Event's payload equals to the newly selected page.
* Will fire only if collection size is set and all values are valid.
* Page numbers start with 1
*/
@Output() pageChange = new EventEmitter(true);
/**
* Pagination display size: small or large
*/
@Input() size: 'sm' | 'lg';
@Input() links = [];
constructor(config: NgbPaginationConfig) {
this.disabled = config.disabled;
this.boundaryLinks = config.boundaryLinks;
this.directionLinks = config.directionLinks;
this.ellipses = config.ellipses;
this.maxSize = config.maxSize;
this.pageSize = config.pageSize;
this.rotate = config.rotate;
this.size = config.size;
this.links = config.links;
}
hasPrevious(): boolean { return this.page > 1; }
hasNext(): boolean { return this.page < this.pageCount; }
selectPage(pageNumber: number): void { this._updatePages(pageNumber); }
ngOnChanges(changes: SimpleChanges): void { this._updatePages(this.page); }
isEllipsis(pageNumber): boolean { return pageNumber === -1; }
/**
* Appends ellipses and first/last page number to the displayed pages
*/
private _applyEllipses(start: number, end: number) {
if (this.ellipses) {
if (start > 0) {
if (start > 1) {
this.pages.unshift(-1);
}
this.pages.unshift(1);
}
if (end < this.pageCount) {
if (end < (this.pageCount - 1)) {
this.pages.push(-1);
}
this.pages.push(this.pageCount);
}
}
}
/**
* Rotates page numbers based on maxSize items visible.
* Currently selected page stays in the middle:
*
* Ex. for selected page = 6:
* [5,*6*,7] for maxSize = 3
* [4,5,*6*,7] for maxSize = 4
*/
private _applyRotation(): [number, number] {
let start = 0;
let end = this.pageCount;
let leftOffset = Math.floor(this.maxSize / 2);
let rightOffset = this.maxSize % 2 === 0 ? leftOffset - 1 : leftOffset;
if (this.page <= leftOffset) {
// very beginning, no rotation -> [0..maxSize]
end = this.maxSize;
} else if (this.pageCount - this.page < leftOffset) {
// very end, no rotation -> [len-maxSize..len]
start = this.pageCount - this.maxSize;
} else {
// rotate
start = this.page - leftOffset - 1;
end = this.page + rightOffset;
}
return [start, end];
}
/**
* Paginates page numbers based on maxSize items per page
*/
private _applyPagination(): [number, number] {
let page = Math.ceil(this.page / this.maxSize) - 1;
let start = page * this.maxSize;
let end = start + this.maxSize;
return [start, end];
}
private _setPageInRange(newPageNo) {
const prevPageNo = this.page;
this.page = getValueInRange(newPageNo, this.pageCount, 1);
if (this.page !== prevPageNo && isNumber(this.collectionSize)) {
this.pageChange.emit(this.page);
}
}
private _updatePages(newPage: number) {
this.pageCount = Math.ceil(this.collectionSize / this.pageSize);
if (!isNumber(this.pageCount)) {
this.pageCount = 0;
}
// fill-in model needed to render pages
this.pages.length = 0;
for (let i = 1; i <= this.pageCount; i++) {
this.pages.push(i);
}
// set page within 1..max range
this._setPageInRange(newPage);
// apply maxSize if necessary
if (this.maxSize > 0 && this.pageCount > this.maxSize) {
let start = 0;
let end = this.pageCount;
// either paginating or rotating page numbers
if (this.rotate) {
[start, end] = this._applyRotation();
} else {
[start, end] = this._applyPagination();
}
this.pages = this.pages.slice(start, end);
// adding ellipses
this._applyEllipses(start, end);
}
}
}