Skip to content

Commit 85fb00a

Browse files
andrewseguinjelbourn
authored andcommitted
feat(pagination): initial pagination component (#5156)
1 parent 7b2d4ae commit 85fb00a

25 files changed

+854
-36
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/demo-app/data-table/data-table-demo.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,11 @@
7878
}">
7979
</cdk-row>
8080
</cdk-table>
81+
82+
<md-paginator #paginator
83+
[length]="_peopleDatabase.data.length"
84+
[pageIndex]="0"
85+
[pageSize]="10"
86+
[pageSizeOptions]="[5, 10, 25, 100]">
87+
</md-paginator>
8188
</div>

src/demo-app/data-table/data-table-demo.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {Component} from '@angular/core';
1+
import {Component, ViewChild} from '@angular/core';
22
import {PeopleDatabase, UserData} from './people-database';
33
import {PersonDataSource} from './person-data-source';
4+
import {MdPaginator} from '@angular/material';
45

56
export type UserProperties = 'userId' | 'userName' | 'progress' | 'color' | undefined;
67

@@ -19,13 +20,17 @@ export class DataTableDemo {
1920
changeReferences = false;
2021
highlights = new Set<string>();
2122

22-
constructor(public _peopleDatabase: PeopleDatabase) {
23+
@ViewChild(MdPaginator) _paginator: MdPaginator;
24+
25+
constructor(public _peopleDatabase: PeopleDatabase) { }
26+
27+
ngOnInit() {
2328
this.connect();
2429
}
2530

2631
connect() {
2732
this.propertiesToDisplay = ['userId', 'userName', 'progress', 'color'];
28-
this.dataSource = new PersonDataSource(this._peopleDatabase);
33+
this.dataSource = new PersonDataSource(this._peopleDatabase, this._paginator);
2934
this._peopleDatabase.initialize();
3035
}
3136

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,58 @@
1-
import {CollectionViewer, DataSource} from '@angular/material';
1+
import {CollectionViewer, DataSource, MdPaginator} from '@angular/material';
22
import {Observable} from 'rxjs/Observable';
33
import {PeopleDatabase, UserData} from './people-database';
4+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
45

56
export class PersonDataSource extends DataSource<any> {
7+
/** Data that should be displayed by the table. */
8+
_displayData = new BehaviorSubject<UserData[]>([]);
9+
10+
/** Cached data provided by the display data. */
611
_renderedData: any[] = [];
712

8-
constructor(private _peopleDatabase: PeopleDatabase) {
13+
constructor(private _peopleDatabase: PeopleDatabase,
14+
private _paginator: MdPaginator) {
915
super();
16+
17+
// Subscribe to page changes and database changes by clearing the cached data and
18+
// determining the updated display data.
19+
Observable.merge(this._paginator.page, this._peopleDatabase.dataChange).subscribe(() => {
20+
this._renderedData = [];
21+
this.updateDisplayData();
22+
});
1023
}
1124

1225
connect(collectionViewer: CollectionViewer): Observable<UserData[]> {
13-
const changeStreams = Observable.combineLatest(
14-
collectionViewer.viewChange,
15-
this._peopleDatabase.dataChange);
16-
return changeStreams.map((result: any[]) => {
17-
const view: {start: number, end: number} = result[0];
26+
this.updateDisplayData();
1827

19-
// Set the rendered rows length to the virtual page size. Fill in the data provided
20-
// from the index start until the end index or pagination size, whichever is smaller.
21-
this._renderedData.length = this._peopleDatabase.data.length;
28+
const streams = [collectionViewer.viewChange, this._displayData];
29+
return Observable.combineLatest(streams)
30+
.map((results: [{start: number, end: number}, UserData[]]) => {
31+
const [view, data] = results;
2232

23-
const buffer = 20;
24-
let rangeStart = Math.max(0, view.start - buffer);
25-
let rangeEnd = Math.min(this._peopleDatabase.data.length, view.end + buffer);
33+
// Set the rendered rows length to the virtual page size. Fill in the data provided
34+
// from the index start until the end index or pagination size, whichever is smaller.
35+
this._renderedData.length = data.length;
2636

27-
for (let i = rangeStart; i < rangeEnd; i++) {
28-
this._renderedData[i] = this._peopleDatabase.data[i];
29-
}
37+
const buffer = 20;
38+
let rangeStart = Math.max(0, view.start - buffer);
39+
let rangeEnd = Math.min(data.length, view.end + buffer);
3040

31-
return this._renderedData;
32-
});
41+
for (let i = rangeStart; i < rangeEnd; i++) {
42+
this._renderedData[i] = data[i];
43+
}
44+
45+
return this._renderedData;
46+
});
47+
}
48+
49+
updateDisplayData() {
50+
const data = this._peopleDatabase.data.slice();
51+
52+
// Grab the page's slice of data.
53+
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
54+
const paginatedData = data.splice(startIndex, this._paginator.pageSize);
55+
56+
this._displayData.next(paginatedData);
3357
}
3458
}

src/demo-app/demo-app-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
MdListModule,
6262
MdMenuModule,
6363
MdNativeDateModule,
64+
MdPaginatorModule,
6465
MdProgressBarModule,
6566
MdProgressSpinnerModule,
6667
MdRadioModule,
@@ -98,6 +99,7 @@ import {TableHeaderDemo} from './data-table/table-header-demo';
9899
MdListModule,
99100
MdMenuModule,
100101
MdCoreModule,
102+
MdPaginatorModule,
101103
MdProgressBarModule,
102104
MdProgressSpinnerModule,
103105
MdRadioModule,

src/lib/core/theming/_all-theme.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
@import '../../input/input-theme';
1515
@import '../../list/list-theme';
1616
@import '../../menu/menu-theme';
17+
@import '../../paginator/paginator-theme';
1718
@import '../../progress-bar/progress-bar-theme';
1819
@import '../../progress-spinner/progress-spinner-theme';
1920
@import '../../radio/radio-theme';
@@ -44,6 +45,7 @@
4445
@include mat-input-theme($theme);
4546
@include mat-list-theme($theme);
4647
@include mat-menu-theme($theme);
48+
@include mat-paginator-theme($theme);
4749
@include mat-progress-bar-theme($theme);
4850
@include mat-progress-spinner-theme($theme);
4951
@include mat-radio-theme($theme);

src/lib/core/typography/_all-typography.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@import '../../input/input-theme';
1313
@import '../../list/list-theme';
1414
@import '../../menu/menu-theme';
15+
@import '../../paginator/paginator-theme';
1516
@import '../../progress-bar/progress-bar-theme';
1617
@import '../../progress-spinner/progress-spinner-theme';
1718
@import '../../radio/radio-theme';
@@ -42,6 +43,7 @@
4243
@include mat-icon-typography($config);
4344
@include mat-input-typography($config);
4445
@include mat-menu-typography($config);
46+
@include mat-paginator-typography($config);
4547
@include mat-progress-bar-typography($config);
4648
@include mat-progress-spinner-typography($config);
4749
@include mat-radio-typography($config);

src/lib/module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {StyleModule} from './core/style/index';
4646
import {MdDatepickerModule} from './datepicker/index';
4747
import {CdkDataTableModule} from './core/data-table/index';
4848
import {MdExpansionModule} from './expansion/index';
49+
import {MdPaginatorModule} from './paginator/index';
4950

5051
const MATERIAL_MODULES = [
5152
MdAutocompleteModule,
@@ -62,6 +63,7 @@ const MATERIAL_MODULES = [
6263
MdInputModule,
6364
MdListModule,
6465
MdMenuModule,
66+
MdPaginatorModule,
6567
MdProgressBarModule,
6668
MdProgressSpinnerModule,
6769
MdRadioModule,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@import '../core/theming/palette';
2+
@import '../core/theming/theming';
3+
@import '../core/typography/typography-utils';
4+
5+
6+
@mixin mat-paginator-theme($theme) {
7+
$foreground: map-get($theme, foreground);
8+
9+
.mat-paginator,
10+
.mat-paginator-page-size .mat-select-trigger {
11+
color: mat-color($foreground, secondary-text);
12+
}
13+
14+
.mat-paginator-increment,
15+
.mat-paginator-decrement {
16+
border-top: 2px solid mat-color($foreground, 'icon');
17+
border-right: 2px solid mat-color($foreground, 'icon');
18+
}
19+
20+
.mat-icon-button[disabled] {
21+
.mat-paginator-increment,
22+
.mat-paginator-decrement {
23+
border-color: mat-color($foreground, 'disabled');
24+
}
25+
}
26+
}
27+
28+
@mixin mat-paginator-typography($config) {
29+
.mat-paginator {
30+
font: {
31+
family: mat-font-family($config);
32+
size: mat-font-size($config, caption);
33+
}
34+
}
35+
36+
.mat-paginator-page-size .mat-select-trigger {
37+
font: {
38+
family: mat-font-family($config);
39+
size: mat-font-size($config, caption);
40+
}
41+
}
42+
}

src/lib/paginator/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {NgModule} from '@angular/core';
10+
import {CommonModule} from '@angular/common';
11+
import {FormsModule} from '@angular/forms';
12+
import {MdCommonModule, OverlayModule} from '../core';
13+
import {MdButtonModule} from '../button/index';
14+
import {MdSelectModule} from '../select/index';
15+
import {MdPaginator} from './paginator';
16+
import {MdPaginatorIntl} from './paginator-intl';
17+
import {MdTooltipModule} from '../tooltip/index';
18+
19+
20+
@NgModule({
21+
imports: [
22+
CommonModule,
23+
FormsModule,
24+
MdButtonModule,
25+
MdSelectModule,
26+
MdTooltipModule,
27+
],
28+
exports: [MdPaginator],
29+
declarations: [MdPaginator],
30+
providers: [MdPaginatorIntl],
31+
})
32+
export class MdPaginatorModule {}
33+
34+
35+
export * from './paginator';

src/lib/paginator/paginator-intl.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Injectable} from '@angular/core';
10+
11+
/**
12+
* To modify the labels and text displayed, create a new instance of MdPaginatorIntl and
13+
* include it in a custom provider
14+
*/
15+
@Injectable()
16+
export class MdPaginatorIntl {
17+
/** A label for the page size selector. */
18+
itemsPerPageLabel = 'Items per page:';
19+
20+
/** A label for the button that increments the current page. */
21+
nextPageLabel = 'Next page';
22+
23+
/** A label for the button that decrements the current page. */
24+
previousPageLabel = 'Previous page';
25+
26+
/** A label for the range of items within the current page and the length of the whole list. */
27+
getRangeLabel = (page: number, pageSize: number, length: number) => {
28+
if (length == 0 || pageSize == 0) { return `0 of ${length}`; }
29+
30+
length = Math.max(length, 0);
31+
32+
const startIndex = page * pageSize;
33+
34+
// If the start index exceeds the list length, do not try and fix the end index to the end.
35+
const endIndex = startIndex < length ?
36+
Math.min(startIndex + pageSize, length) :
37+
startIndex + pageSize;
38+
39+
return `${startIndex + 1} - ${endIndex} of ${length}`;
40+
}
41+
}

src/lib/paginator/paginator.html

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<div class="mat-paginator-page-size">
2+
<div class="mat-paginator-page-size-label">
3+
{{_intl.itemsPerPageLabel}}
4+
</div>
5+
6+
<md-select *ngIf="_displayedPageSizeOptions.length > 1"
7+
class="mat-paginator-page-size-select"
8+
[ngModel]="pageSize"
9+
[aria-label]="_intl.itemsPerPageLabel"
10+
(change)="_changePageSize($event.value)">
11+
<md-option *ngFor="let pageSizeOption of _displayedPageSizeOptions" [value]="pageSizeOption">
12+
{{pageSizeOption}}
13+
</md-option>
14+
</md-select>
15+
16+
<div *ngIf="_displayedPageSizeOptions.length <= 1">{{pageSize}}</div>
17+
</div>
18+
19+
<div class="mat-paginator-range-label">
20+
{{_intl.getRangeLabel(pageIndex, pageSize, length)}}
21+
</div>
22+
23+
<button md-icon-button
24+
class="mat-paginator-navigation-previous"
25+
(click)="previousPage()"
26+
[attr.aria-label]="_intl.previousPageLabel"
27+
[mdTooltip]="_intl.previousPageLabel"
28+
[mdTooltipPosition]="'above'"
29+
[disabled]="!hasPreviousPage()">
30+
<div class="mat-paginator-increment"></div>
31+
</button>
32+
<button md-icon-button
33+
class="mat-paginator-navigation-next"
34+
(click)="nextPage()"
35+
[attr.aria-label]="_intl.nextPageLabel"
36+
[mdTooltip]="_intl.nextPageLabel"
37+
[mdTooltipPosition]="'above'"
38+
[disabled]="!hasNextPage()">
39+
<div class="mat-paginator-decrement"></div>
40+
</button>

0 commit comments

Comments
 (0)