diff --git a/apps/docs/src/app/core/component-docs/pagination/examples/pagination-example.component.ts b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-example.component.ts index 0857d68317c..e0e5bdcea2a 100644 --- a/apps/docs/src/app/core/component-docs/pagination/examples/pagination-example.component.ts +++ b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-example.component.ts @@ -1,48 +1,53 @@ import { Component, ViewChild } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { PaginationComponent } from '@fundamental-ngx/core'; +import { tap, delay } from 'rxjs/operators'; @Component({ selector: 'fd-pagination-example', template: `

- ` + +
{{ notification }}
+ ` }) export class PaginationExampleComponent { totalItems = 50; itemsPerPage = 10; - currentPage = 3; + currentPage = 5; + notification: string = null; @ViewChild(PaginationComponent) paginationComponent: PaginationComponent; - newPageClicked(event): void { - this.http.get('assets/pagination-data.json').subscribe( + newPageClicked(event: number): void { + this.http.get('assets/pagination-data.json').pipe( + tap(() => { + this.notification = 'loading...'; + }), + delay(100), + ).subscribe( (data) => { - /* - update the currentPage when the http action is successful - */ + /* update the currentPage when the http action is successful */ this.currentPage = event; - console.log('page change success!'); + this.notification = 'page change success!'; }, (error) => { - /* - do not update the currentPage when the http action fails - */ - console.log('page change error!'); + /* do not update the currentPage when the http action fails */ + this.notification = 'page change error!'; }, () => { - alert('New page selected: ' + this.currentPage); + this.notification = 'New page selected: ' + this.currentPage; } ); } - goToPage1(): void { - this.paginationComponent.goToPage(1); + goToPage(page: number): void { + this.paginationComponent.goToPage(page); } constructor(private http: HttpClient) {} diff --git a/apps/docs/src/app/core/component-docs/pagination/examples/pagination-per-page-example.component.html b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-per-page-example.component.html new file mode 100644 index 00000000000..0cc6b4c6a00 --- /dev/null +++ b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-per-page-example.component.html @@ -0,0 +1,37 @@ +
+
[itemsPerPage]=15 Default property for items per page
+ +
+
+
[itemsPerPageOptions]="[4,8,16]" - Default select template for items per page options
+ +
+
+
Custom items per page - list of buttons
+ + + + + + + +
\ No newline at end of file diff --git a/apps/docs/src/app/core/component-docs/pagination/examples/pagination-per-page-example.component.ts b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-per-page-example.component.ts new file mode 100644 index 00000000000..157a0c3db83 --- /dev/null +++ b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-per-page-example.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'fd-pagination-per-page-example', + templateUrl: './pagination-per-page-example.component.html' +}) +export class PaginationPerPageExampleComponent { + totalItems = 50; + currentPage1 = 1; + currentPage2 = 1; + currentPage3 = 1; + + pageChanged1(event: number): void { + this.currentPage1 = event; + } + pageChanged2(event: number): void { + this.currentPage2 = event; + } + pageChanged3(event: number): void { + this.currentPage3 = event; + } +} diff --git a/apps/docs/src/app/core/component-docs/pagination/examples/pagination-showing-example.component.ts b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-showing-example.component.ts new file mode 100644 index 00000000000..6972b8893ac --- /dev/null +++ b/apps/docs/src/app/core/component-docs/pagination/examples/pagination-showing-example.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'fd-pagination-showing-example', + template: ` + + + From {{ showing.from }} to {{ showing.to }}. Total items {{ showing.of }} + + ` +}) +export class PaginationShowingExampleComponent { + totalItems = 50; + itemsPerPage = 10; + currentPage = 1; + + pageChanged(event: number): void { + this.currentPage = event; + } +} diff --git a/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.html b/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.html index 5aaee5e4ee6..92f5281f1f6 100644 --- a/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.html +++ b/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.html @@ -1,5 +1,5 @@ - - Pagination + + Basic Pagination @@ -7,11 +7,47 @@ + + + Pagination showing items + + + Pagination with custom showing template +
    +
  • Set [displayTextTemplate] property to specify the display template for showing items.
  • +
+
+ + + + + + + + + Pagination with per page select. + + + Pagination with custom showing template +
    +
  • Set [itemsPerPage] property to number of items per page. It's equal 10 by default.
  • +
  • Set [itemsPerPageOptions] property to array of numbers for items per page select.
  • +
  • Set [itemsPerPageTemplate] property to custom template. + You will have onSelect property to interact with component. + This property has higher priority than [itemsPerPageOptions].
  • +
+
+ + + + + + + diff --git a/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.ts b/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.ts index 31bc2eca2be..c6b70ed5220 100644 --- a/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.ts +++ b/apps/docs/src/app/core/component-docs/pagination/pagination-docs.component.ts @@ -3,6 +3,12 @@ import { Schema } from '../../../schema/models/schema.model'; import { SchemaFactoryService } from '../../../schema/services/schema-factory/schema-factory.service'; import * as paginationSrc from '!raw-loader!./examples/pagination-example.component.ts'; + +import * as paginationShowingSrc from '!raw-loader!./examples/pagination-showing-example.component.ts'; + +import * as paginationPerPageHtml from '!raw-loader!./examples/pagination-per-page-example.component.html'; +import * as paginationPerPageTs from '!raw-loader!./examples/pagination-per-page-example.component.ts'; + import { ExampleFile } from '../../../documentation/core-helpers/code-example/example-file'; @Component({ @@ -53,6 +59,28 @@ export class PaginationDocsComponent { } ]; + paginationShowing: ExampleFile[] = [ + { + language: 'typescript', + code: paginationShowingSrc, + fileName: 'pagination-showing-example', + component: 'PaginationShowingExampleComponent' + } + ]; + paginationPerPageSrc: ExampleFile[] = [ + { + language: 'html', + code: paginationPerPageHtml, + fileName: 'pagination-perpage-example', + }, + { + language: 'typescript', + code: paginationPerPageTs, + fileName: 'pagination-perpage-example', + component: 'PaginationPerPageExampleComponent' + } + ]; + constructor(private schemaFactory: SchemaFactoryService) { this.schema = this.schemaFactory.getComponent('pagination'); } diff --git a/apps/docs/src/app/core/component-docs/pagination/pagination-docs.module.ts b/apps/docs/src/app/core/component-docs/pagination/pagination-docs.module.ts index a1a21937738..84cdc5d1d18 100644 --- a/apps/docs/src/app/core/component-docs/pagination/pagination-docs.module.ts +++ b/apps/docs/src/app/core/component-docs/pagination/pagination-docs.module.ts @@ -5,7 +5,10 @@ import { API_FILES } from '../../api-files'; import { PaginationHeaderComponent } from './pagination-header/pagination-header.component'; import { PaginationDocsComponent } from './pagination-docs.component'; import { PaginationExampleComponent } from './examples/pagination-example.component'; -import { PaginationModule } from '@fundamental-ngx/core'; +import { PaginationShowingExampleComponent } from './examples/pagination-showing-example.component'; +import { PaginationPerPageExampleComponent } from './examples/pagination-per-page-example.component'; + +import { PaginationModule, ToolbarModule, SelectModule, SegmentedButtonModule } from '@fundamental-ngx/core'; import { SharedDocumentationPageModule } from '../../../documentation/shared-documentation-page.module'; const routes: Routes = [ @@ -20,8 +23,21 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes), SharedDocumentationPageModule, PaginationModule], + imports: [ + RouterModule.forChild(routes), + SharedDocumentationPageModule, + PaginationModule, + ToolbarModule, + SelectModule, + SegmentedButtonModule + ], exports: [RouterModule], - declarations: [PaginationDocsComponent, PaginationHeaderComponent, PaginationExampleComponent] + declarations: [ + PaginationDocsComponent, + PaginationHeaderComponent, + PaginationExampleComponent, + PaginationShowingExampleComponent, + PaginationPerPageExampleComponent + ] }) export class PaginationDocsModule {} diff --git a/libs/core/src/lib/pagination/pagination.component.html b/libs/core/src/lib/pagination/pagination.component.html index 0eb12919749..2b64c5be8ed 100644 --- a/libs/core/src/lib/pagination/pagination.component.html +++ b/libs/core/src/lib/pagination/pagination.component.html @@ -1,51 +1,72 @@ - - - {{ totalItems }} {{ displayText }} - - +
+
+ + + + +
+
+ +
+
- + - -
+ + + Showing {{ showing.from }}-{{ showing.to }} of {{ showing.of }} + + + + + {{ itemsPerPageLabel }}: + + {{ option }} + + diff --git a/libs/core/src/lib/pagination/pagination.component.scss b/libs/core/src/lib/pagination/pagination.component.scss index edea6b942fd..aec87ac39a5 100644 --- a/libs/core/src/lib/pagination/pagination.component.scss +++ b/libs/core/src/lib/pagination/pagination.component.scss @@ -1,10 +1,20 @@ @import '~fundamental-styles/dist/pagination'; -.fd-pagination-direction-override-display { - display: inline-block; -} +$block: fd-pagination; + +.#{$block} { + &-direction-override-display { + display: inline-block; + } + + &__total--rtl { + margin-right: 0; + margin-left: 8px; + } -.fd-pagination__total--rtl { - margin-right: 0; - margin-left: 8px; + &__holder { + display: flex; + align-items: center; + justify-content: space-between; + } } diff --git a/libs/core/src/lib/pagination/pagination.component.spec.ts b/libs/core/src/lib/pagination/pagination.component.spec.ts index 8b82f0bdd56..d7e14edd5f3 100644 --- a/libs/core/src/lib/pagination/pagination.component.spec.ts +++ b/libs/core/src/lib/pagination/pagination.component.spec.ts @@ -1,6 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy } from '@angular/core'; +import { By } from '@angular/platform-browser'; import { PaginationComponent } from './pagination.component'; import { PaginationService } from './pagination.service'; +import { SelectModule, SelectComponent } from '../select/public_api'; describe('Pagination Test', () => { let component: PaginationComponent; @@ -12,7 +15,10 @@ describe('Pagination Test', () => { TestBed.configureTestingModule({ declarations: [PaginationComponent], + imports: [SelectModule], providers: [{ provide: PaginationService, useValue: paginationSpy }] + }).overrideComponent(PaginationComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); paginationServiceSpy = TestBed.get(PaginationService); @@ -27,6 +33,18 @@ describe('Pagination Test', () => { fixture.detectChanges(); }); + it('should default to first page', () => { + component.currentPage = null; + fixture.detectChanges(); + expect(component.currentPage).toEqual(1); + }); + + it('should get the pagination object for the service', () => { + const retVal = component.getPaginationObject(); + + expect(retVal).toEqual({ totalItems: 3, currentPage: 1, itemsPerPage: 2 }); + }); + it('should handle keypress', () => { const keyboardEvent = new KeyboardEvent('keypress', { key: 'Enter' @@ -40,6 +58,15 @@ describe('Pagination Test', () => { expect(component.goToPage).toHaveBeenCalledWith(1); }); + it('should have default select for item per page with options', async () => { + component.totalItems = 100; + component.itemsPerPageOptions = [2, 4, 6]; + fixture.detectChanges(); + const selectComponent = fixture.debugElement.query(By.directive(SelectComponent)); + const selectInstance = selectComponent.injector.get(SelectComponent); + expect(selectInstance).toBeInstanceOf(SelectComponent); + }); + it('should handle mouseevent', () => { const mouseEvent = new MouseEvent('click'); spyOn(mouseEvent, 'preventDefault'); @@ -49,10 +76,4 @@ describe('Pagination Test', () => { expect(component.pageChangeStart.emit).toHaveBeenCalledWith(1); }); - - it('should get the pagination object for the service', () => { - const retVal = component.getPaginationObject(); - - expect(retVal).toEqual({ totalItems: 3, currentPage: 1, itemsPerPage: 2 }); - }); }); diff --git a/libs/core/src/lib/pagination/pagination.component.ts b/libs/core/src/lib/pagination/pagination.component.ts index c1a71d64040..1250bff5390 100644 --- a/libs/core/src/lib/pagination/pagination.component.ts +++ b/libs/core/src/lib/pagination/pagination.component.ts @@ -8,13 +8,27 @@ import { Optional, Output, SimpleChanges, - ViewEncapsulation + ViewEncapsulation, + TemplateRef } from '@angular/core'; +import { + ENTER, + SPACE +} from '@angular/cdk/keycodes'; +import { coerceNumberProperty, coerceArray } from '@angular/cdk/coercion'; + +import { KeyUtil } from '../utils/functions'; import { PaginationService } from './pagination.service'; import { RtlService } from '../utils/services/rtl.service'; -import { BehaviorSubject } from 'rxjs'; import { Pagination } from './pagination.model'; +/** Constant representing the default number of items per page. */ +const DEFAULT_ITEMS_PER_PAGE = 10; +interface CurrentShowing { + from: number; + to: number; + of: number; +}; /** * The component that is used to provide navigation between paged information. * ```html @@ -41,7 +55,8 @@ import { Pagination } from './pagination.model'; ], encapsulation: ViewEncapsulation.None, styleUrls: ['./pagination.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, + preserveWhitespaces: true }) export class PaginationComponent implements OnChanges, OnInit { /** Represents the total number of items. */ @@ -50,22 +65,57 @@ export class PaginationComponent implements OnChanges, OnInit { /** Represents the current page number. */ @Input() - currentPage: number; + get currentPage(): number { + return this._currentPage; + }; + set currentPage(value: number) { + this._currentPage = coerceNumberProperty(value, 1); + }; /** Represents the number of items per page. */ @Input() - itemsPerPage: number; + get itemsPerPage(): number { + return this._itemsPerPage; + } + set itemsPerPage(value: number) { + this._itemsPerPage = Math.min(coerceNumberProperty(value, DEFAULT_ITEMS_PER_PAGE), this.totalItems); + } + + /** + * The custom template show range of item by current page of items. + * It has higher priority than `itemsPerPageOptions` property. + */ + @Input() + itemsPerPageTemplate: TemplateRef; + + /** Label for options for items per page. */ + @Input() + itemsPerPageLabel = 'Results per page'; + + /** Represents the options for items per page. */ + @Input() + get itemsPerPageOptions(): number[] { + return this._itemsPerPageOptions; + } + set itemsPerPageOptions(value: number[]) { + this._itemsPerPageOptions = coerceArray(value) + .filter(v => v > 0 && v < this.totalItems) + .sort((a, b) => a - b); + if (this._itemsPerPageOptions.some(v => v !== this.itemsPerPage)) { + this.itemsPerPage = this._itemsPerPageOptions[0]; + } + } /** Whether to display the total number of items. */ @Input() displayTotalItems = true; /** - * The text appended to the total number of items. - * The default text is set to 'items' + * The template show range of item by current page of items. + * Default view: Showing {{ from }}-{{ to }} of {{ of }} */ @Input() - displayText = 'items'; + displayTextTemplate: TemplateRef; /** Label for the 'previous' page button. */ @Input() @@ -83,17 +133,51 @@ export class PaginationComponent implements OnChanges, OnInit { rtl = false; /** @hidden */ - get customClasses(): string { + get rtlClass(): string { return this.rtl ? 'fd-pagination__total--rtl' : ''; } /** @hidden */ - pages$: BehaviorSubject = new BehaviorSubject([]); + pages: number[] = []; + /** @hidden */ + get isFirstPage(): boolean { + return this.currentPage === 1; + }; + get isLastPage(): boolean { + return this.currentPage === this.paginationService.getTotalPages(this.getPaginationObject()); + }; - isLastPage$: BehaviorSubject = new BehaviorSubject(this._isLastPage); + get currentShowing(): CurrentShowing { + return this._currentShowing; + }; + /** @hidden */ + private _currentShowing: CurrentShowing = { + from: 0, + to: 0, + of: 0 + }; + /** @hidden */ + private _itemsPerPage: number = DEFAULT_ITEMS_PER_PAGE; + /** @hidden */ + private _itemsPerPageOptions: number[]; + /** @hidden */ + private _currentPage = 1; + + /** @hidden */ + constructor ( + private readonly paginationService: PaginationService, + @Optional() private readonly rtlService: RtlService + ) {} /** @hidden */ - constructor(private paginationService: PaginationService, @Optional() private rtlService: RtlService) {} + onChangePerPage = (event: number) => { + this.itemsPerPage = event; + this._refreshPages(); + const maxPage = this.pages[this.pages.length - 1]; + if (this.currentPage > maxPage) { + this.pageChangeStart.emit(maxPage); + } + }; /** @hidden */ ngOnChanges(changes: SimpleChanges): void { @@ -103,14 +187,13 @@ export class PaginationComponent implements OnChanges, OnInit { this._refreshPages(); - const totalPages = this.paginationService.getTotalPages(this.getPaginationObject()); + const pagination = this.getPaginationObject(); + const totalPages = this.paginationService.getTotalPages(pagination); if (!this.currentPage || this.currentPage < 1) { this.currentPage = 1; } else if (this.currentPage > totalPages) { this.currentPage = totalPages; } - - this.isLastPage$.next(this._isLastPage); } /** @hidden */ @@ -129,7 +212,7 @@ export class PaginationComponent implements OnChanges, OnInit { * @param $event The keyboard event. */ onKeypressHandler(page: number, $event: KeyboardEvent): void { - if ($event.key === ' ' || $event.key === 'Enter') { + if (KeyUtil.isKeyCode($event, SPACE) || KeyUtil.isKeyCode($event, ENTER)) { $event.preventDefault(); this.goToPage(page); } @@ -147,12 +230,25 @@ export class PaginationComponent implements OnChanges, OnInit { if (page > this.paginationService.getTotalPages(this.getPaginationObject()) || page < 1) { return; } - this._refreshPages(); this.pageChangeStart.emit(page); } + /** + * Navigates to a previous page. + */ + previousPage(): void { + this.goToPage(this.currentPage - 1); + } + + /** + * Navigates to a next page. + */ + nextPage(): void { + this.goToPage(this.currentPage + 1); + } + /** * Retrieves an object that represents * the total number of items, the current page, and the number of items per page. @@ -167,13 +263,15 @@ export class PaginationComponent implements OnChanges, OnInit { /** @hidden */ private _refreshPages(): void { - let pages = this.paginationService.getPages(this.getPaginationObject()); - pages = this.rtl ? pages.slice().reverse() : pages; - this.pages$.next(pages); - } + const pagination = this.getPaginationObject(); + let pages = this.paginationService.getPages(pagination); + if (this.rtl) { + pages = pages.slice().reverse(); + } + this.pages = pages; - /** @hidden */ - private get _isLastPage(): boolean { - return this.currentPage === this.paginationService.getTotalPages(this.getPaginationObject()); + this._currentShowing.from = this.currentPage - 1 === 0 ? 1 : (this.currentPage - 1) * pagination.itemsPerPage + 1; + this._currentShowing.to = Math.min((this.currentPage - 1) * pagination.itemsPerPage + pagination.itemsPerPage, this.totalItems); + this._currentShowing.of = this.totalItems; } } diff --git a/libs/core/src/lib/pagination/pagination.module.ts b/libs/core/src/lib/pagination/pagination.module.ts index cf223650e03..1bcd56af1ef 100644 --- a/libs/core/src/lib/pagination/pagination.module.ts +++ b/libs/core/src/lib/pagination/pagination.module.ts @@ -4,11 +4,12 @@ import { CommonModule } from '@angular/common'; import { PaginationComponent } from './pagination.component'; import { ButtonModule } from '../button/button.module'; import { IconModule } from '../icon/icon.module'; +import { SelectModule } from '../select/select.module'; import { PaginationService } from './pagination.service'; @NgModule({ declarations: [PaginationComponent], - imports: [CommonModule, ButtonModule, IconModule], + imports: [CommonModule, ButtonModule, IconModule, SelectModule], providers: [PaginationService], exports: [PaginationComponent] }) diff --git a/libs/core/src/lib/pagination/pagination.service.spec.ts b/libs/core/src/lib/pagination/pagination.service.spec.ts index 9dd1faa35e2..3e79f9152f8 100644 --- a/libs/core/src/lib/pagination/pagination.service.spec.ts +++ b/libs/core/src/lib/pagination/pagination.service.spec.ts @@ -1,4 +1,4 @@ -import { TestBed, inject } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { PaginationService } from './pagination.service'; import { Pagination } from './pagination.model'; @@ -9,7 +9,7 @@ describe('PaginationService', () => { TestBed.configureTestingModule({ providers: [PaginationService] }); - service = TestBed.get(PaginationService); + service = TestBed.inject(PaginationService); }); it('should be created', () => { @@ -17,7 +17,7 @@ describe('PaginationService', () => { }); it('should default to 10 pages per page', () => { - const pages = service.getPages({ totalItems: 10, itemsPerPage: null }); + const pages = service.getPages({ totalItems: 10, itemsPerPage: 10 }); expect(pages.length).toEqual(1); }); @@ -42,22 +42,6 @@ describe('PaginationService', () => { expect(total).toEqual(3); }); - it('should default to 10 items per page', () => { - const pagination: Pagination = { - totalItems: 10 - }; - service.validate(pagination); - expect(pagination.itemsPerPage).toEqual(service.DEFAULT_ITEMS_PER_PAGE); - }); - - it('should default to first page', () => { - const pagination: Pagination = { - totalItems: 10 - }; - service.validate(pagination); - expect(pagination.currentPage).toEqual(1); - }); - it('should calc 3 pages', () => { const pagination: Pagination = { totalItems: 30, @@ -69,11 +53,11 @@ describe('PaginationService', () => { it('should have one dots section', () => { const pagination: Pagination = { - totalItems: 60, + totalItems: 100, itemsPerPage: 10 }; const pages = service.getPages(pagination); - expect(pages[3]).toEqual(service.MORE); + expect(pages[3]).toEqual(service.buffer); }); it('should have two dots sections', () => { @@ -83,8 +67,8 @@ describe('PaginationService', () => { currentPage: 4 }; const pages = service.getPages(pagination); - expect(pages[1]).toEqual(service.MORE); - expect(pages[5]).toEqual(service.MORE); + expect(pages[1]).toEqual(service.buffer); + expect(pages[5]).toEqual(service.buffer); }); it('should not have two dots sections if second to last page is currentPage', () => { @@ -94,8 +78,8 @@ describe('PaginationService', () => { currentPage: 73 }; const pages = service.getPages(pagination); - expect(pages[1]).toEqual(service.MORE); - expect(pages[5]).not.toEqual(service.MORE); + expect(pages[1]).toEqual(service.buffer); + expect(pages[5]).not.toEqual(service.buffer); expect(pages[5]).toEqual(75); }); }); diff --git a/libs/core/src/lib/pagination/pagination.service.ts b/libs/core/src/lib/pagination/pagination.service.ts index 318b7bf7917..8fb94f3752f 100644 --- a/libs/core/src/lib/pagination/pagination.service.ts +++ b/libs/core/src/lib/pagination/pagination.service.ts @@ -1,7 +1,10 @@ import { Injectable, isDevMode } from '@angular/core'; + import { Pagination } from './pagination.model'; -const DISPLAY_NUM_PAGES = 3; +/** Constant representing the number of pages which appear before and after current page. */ +const SIDE_CURRENT_DISPLAY_PAGES = 1; +const MIN_CORNER_DISPLAY_PAGES = 2; /** * Service that is used to retrieve all the pages, @@ -10,62 +13,51 @@ const DISPLAY_NUM_PAGES = 3; */ @Injectable() export class PaginationService { - /** Constant representing the default number of items per page. */ - public DEFAULT_ITEMS_PER_PAGE = 10; - - /** @hidden */ - public MORE = -1; - /** @hidden */ - constructor() {} - + buffer = -1; /** * Returns a number array representing the pages of the pagination object. * @param pagination An object of type *Pagination*. */ - public getPages(pagination: Pagination): number[] { + getPages(pagination: Pagination): number[] { + if (!pagination.currentPage) { + pagination.currentPage = 1; + } const pages = []; this.validate(pagination); const totalPages = this.getTotalPages(pagination); - - if (totalPages <= DISPLAY_NUM_PAGES) { + if (totalPages <= SIDE_CURRENT_DISPLAY_PAGES * 2 + MIN_CORNER_DISPLAY_PAGES + 1 + 2 ) { for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { - if (pagination.currentPage <= DISPLAY_NUM_PAGES) { - for (let i = 1; i <= DISPLAY_NUM_PAGES; i++) { + for (let i = 1; i <= totalPages; i++) { + if (i === pagination.currentPage) { pages.push(i); - } - if (totalPages !== DISPLAY_NUM_PAGES + 1 && totalPages !== DISPLAY_NUM_PAGES + 2) { - pages.push(this.MORE); - } else if (totalPages === DISPLAY_NUM_PAGES + 2) { - pages.push(DISPLAY_NUM_PAGES + 1); - } - pages.push(totalPages); - } else if (pagination.currentPage > totalPages - (DISPLAY_NUM_PAGES - 1)) { - pages.push(1); - if (totalPages !== DISPLAY_NUM_PAGES + 1 && totalPages !== DISPLAY_NUM_PAGES + 2) { - pages.push(this.MORE); - } else if (totalPages === DISPLAY_NUM_PAGES + 2) { - pages.push(DISPLAY_NUM_PAGES - 1); - } - for (let i = totalPages - (DISPLAY_NUM_PAGES - 1); i <= totalPages; i++) { + } else if (pagination.currentPage <= SIDE_CURRENT_DISPLAY_PAGES + && i <= MIN_CORNER_DISPLAY_PAGES + 1) { pages.push(i); - } - } else { - pages.push(1); - if (totalPages !== DISPLAY_NUM_PAGES + 1) { - pages.push(this.MORE); - } - const buffer = Math.floor(DISPLAY_NUM_PAGES / 2); - for (let i = pagination.currentPage - buffer; i <= pagination.currentPage + buffer; i++) { + } else if (pagination.currentPage >= totalPages - SIDE_CURRENT_DISPLAY_PAGES + && i >= totalPages - MIN_CORNER_DISPLAY_PAGES) { pages.push(i); + } else { + if (i === 1) { + pages.push(i); + } else if (i === totalPages) { + pages.push(i); + } + // tslint:disable-next-line + else if (i >= pagination.currentPage - SIDE_CURRENT_DISPLAY_PAGES + && i < pagination.currentPage + SIDE_CURRENT_DISPLAY_PAGES + 1) { + pages.push(i); + } } - if (totalPages !== DISPLAY_NUM_PAGES + 1 && pagination.currentPage !== totalPages - 2) { - pages.push(this.MORE); - } - pages.push(totalPages); + } + if (pagination.currentPage > SIDE_CURRENT_DISPLAY_PAGES + 2) { + pages.splice(1, 0, this.buffer); + } + if (pagination.currentPage < totalPages - (SIDE_CURRENT_DISPLAY_PAGES + 1)) { + pages.splice(pages.length - 1, 0, this.buffer); } } return pages; @@ -75,10 +67,7 @@ export class PaginationService { * Retrieves the total number of pages. * @param pagination An object of type *Pagination*. */ - public getTotalPages(pagination: Pagination): number { - if (pagination.itemsPerPage <= 0) { - pagination.itemsPerPage = this.DEFAULT_ITEMS_PER_PAGE; - } + getTotalPages(pagination: Pagination): number { return Math.ceil(pagination.totalItems / pagination.itemsPerPage); } @@ -86,17 +75,17 @@ export class PaginationService { * Provides validation for the pagination object. * @param pagination An object of type *Pagination*. */ - public validate(pagination: Pagination): void { - if (!pagination.totalItems && isDevMode()) { - console.warn(`No pages provided in the Pagination object. This warning only appears in development mode.`); - } - if (!pagination.itemsPerPage) { - pagination.itemsPerPage = this.DEFAULT_ITEMS_PER_PAGE; - } else if (pagination.itemsPerPage < 0 && isDevMode()) { - console.warn(`itemsPerPage must be greater than zero. This warning only appears in development mode.`); - } - if (!pagination.currentPage) { - pagination.currentPage = 1; + validate(pagination: Pagination): void { + if (isDevMode()) { + if (isNaN(pagination.totalItems) && !pagination.totalItems) { + console.warn(`No pages provided in the Pagination object. This warning only appears in development mode.`); + } + if (isNaN(pagination.itemsPerPage) && pagination.itemsPerPage <= 0) { + console.warn(`itemsPerPage must be greater than zero. This warning only appears in development mode.`); + } + if (!pagination.currentPage) { + pagination.currentPage = 1; + } } } }