Skip to content

Commit

Permalink
feat(data-table): initial prototype with sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewseguin committed Mar 8, 2017
1 parent 0560eab commit d1e8ad9
Show file tree
Hide file tree
Showing 18 changed files with 1,232 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/demo-app/data-table/data-table-demo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
md-data-table {
padding: 16px;
}
33 changes: 33 additions & 0 deletions src/demo-app/data-table/data-table-demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<md-input-container>
<input mdInput name="searchTerm"
placeholder="Data filter"
[ngModel]="dataSource.filter"
(ngModelChange)="onSearchChanged($event)">
</md-input-container>

<md-data-table class="mat-elevation-z1"
[dataSource]="dataSource"
(onSort)="onSort($event)"
[initialSortColumn]="'name'"
[defaultSortOrder]="'ascending'">
<md-header-row>
<md-header-cell sortKey="name">Name</md-header-cell>
<md-header-cell sortKey="movie">Movie</md-header-cell>
</md-header-row>
<md-row *mdRowContext="let characterData = row; when: characterIsVillan" (click)="rowClicked(characterData)">
<md-cell>{{characterData.name}} (villan).</md-cell>
<md-cell>{{characterData.movie}}</md-cell>
</md-row>
<md-row *mdRowContext="let characterData = row; when: lastCharacterDisplayed" (click)="rowClicked(characterData)">
<md-cell>{{characterData.name}} is last.</md-cell>
<md-cell>{{characterData.movie}}</md-cell>
</md-row>
<md-row *mdRowContext="let characterData = row" (click)="rowClicked(characterData)">
<md-cell>{{characterData.name}}</md-cell>
<md-cell>{{characterData.movie}}</md-cell>
</md-row>
</md-data-table>

<p *ngIf="lastRowClicked">
You just clicked on: {{lastRowClicked.name}} from {{lastRowClicked.movie}}
</p>
39 changes: 39 additions & 0 deletions src/demo-app/data-table/data-table-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {Component} from '@angular/core';
import {TableDemoDataSource, Character} from './demo-data-source';
import {MdTableSortData, NgForContext} from '@angular/material';


@Component({
moduleId: module.id,
selector: 'data-table-demo',
templateUrl: 'data-table-demo.html',
styleUrls: ['data-table-demo.css']
})
export class DataTableDemo {
dataSource = new TableDemoDataSource();

lastRowClicked: Character;

onSearchChanged(val: string) {
this.dataSource.filter = val;
this.dataSource.loadTableRows();
}

onSort(event: MdTableSortData) {
this.dataSource.sortOrder = event.sortOrder;
this.dataSource.sortColumn = event.sortColumn;
this.dataSource.loadTableRows();
}

characterIsVillan(row: Character): boolean {
return row.villan;
}

lastCharacterDisplayed(row: Character, context: NgForContext) {
return context.last;
}

rowClicked(row: Character) {
this.lastRowClicked = row;
}
}
106 changes: 106 additions & 0 deletions src/demo-app/data-table/demo-data-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {Observable} from 'rxjs/Observable';
import {BehaviorSubject} from 'rxjs/Rx';
import {MdTableSortOrder, MdTableDataSource, MdTableRows} from '@angular/material';

export interface Character {
name: string;
movie: string;
villan?: boolean;
}

export const CHARACTERS = [
{name: 'Goofy', movie: 'A Goofy Movie'},
{name: 'Tinker Bell', movie: 'Peter Pan'},
{name: 'Thumper', movie: 'Bambi'},
{name: 'Mad Hatter', movie: 'Alice in Wonderland'},
{name: 'Kronk', movie: 'The Emperor\'s New Groove', villan: true},
{name: 'Gus Gus', movie: 'Cinderella'},
{name: 'Jiminy Cricket', movie: 'Pinocchio'},
{name: 'Tigger', movie: 'Winnie the Pooh'},
{name: 'Gaston', movie: 'Beauty and the Beast', villan: true},
{name: 'Dumbo', movie: 'Dumbo'},
{name: 'Jafar', movie: 'Aladdin', villan: true},
{name: 'Lilo', movie: 'Lilo and Stitch'},
{name: 'Sebastian', movie: 'The Little Mermaid'},
{name: 'Jane', movie: 'Tarzan'},
{name: 'Pumbaa', movie: 'The Lion King'},
{name: 'Mulan', movie: 'Mulan'},
];

export class TableDemoDataSource implements MdTableDataSource<Character> {
private readonly rowSubject =
new BehaviorSubject<MdTableRows<Character>>({rows: [], rowCount: 0});

filter: string;
sortOrder: MdTableSortOrder;
sortColumn: string;

constructor() {
this.loadTableRows();
}

/**
* Returns an observable the table watches in order to update rows.
* @override
*/
getRows(): Observable<MdTableRows<Character>> {
return this.rowSubject.asObservable();
}

/**
* Updates the table based on the table settings and filters.
*/
loadTableRows() {
this.getRowsFromServer().subscribe(filteredRows => {
const rows = {rows: filteredRows, rowCount: filteredRows.length};
this.rowSubject.next(rows);
});
}

/**
* Simulates getting a list of filtered rows from the server with a delay.
*/
getRowsFromServer(): Observable<Character[]> {
const filteredRows = CHARACTERS.filter(this.matchesSearchTerm.bind(this));
if (this.sortColumn) {
filteredRows.sort(this.compareRows.bind(this));
if (this.sortOrder === 'descending') {
filteredRows.reverse();
}
}

return Observable.of(filteredRows);
}

private matchesSearchTerm(row: Character): boolean {
if (!this.filter) {
return true; // Everything matches.
}

return (row.name + row.movie).toLowerCase().indexOf(this.filter.toLowerCase()) != -1;
}

private compareRows(a: Character, b: Character): number {
if (!this.sortColumn) { return 0; }

let valueA: string;
let valueB: string;
if (this.sortColumn == 'name') {
valueA = a.name;
valueB = b.name;
} else if (this.sortColumn == 'movie') {
valueA = a.movie;
valueB = b.movie;
}

// For arbitrary objects, if the valueOf method is overridden, then
// comparison will use that. Otherwise, sorting will do nothing.
if (valueA < valueB) {
return -1;
} else if (valueA > valueB) {
return 1;
} else {
return 0;
}
}
}
2 changes: 2 additions & 0 deletions src/demo-app/demo-app-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {PlatformDemo} from './platform/platform-demo';
import {AutocompleteDemo} from './autocomplete/autocomplete-demo';
import {InputDemo} from './input/input-demo';
import {StyleDemo} from './style/style-demo';
import {DataTableDemo} from './data-table/data-table-demo';

@NgModule({
imports: [
Expand All @@ -62,6 +63,7 @@ import {StyleDemo} from './style/style-demo';
CardDemo,
ChipsDemo,
CheckboxDemo,
DataTableDemo,
DemoApp,
DialogDemo,
GesturesDemo,
Expand Down
1 change: 1 addition & 0 deletions src/demo-app/demo-app/demo-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class DemoApp {
{name: 'Card', route: 'card'},
{name: 'Chips', route: 'chips'},
{name: 'Checkbox', route: 'checkbox'},
{name: 'Data Table', route: 'data-table'},
{name: 'Dialog', route: 'dialog'},
{name: 'Gestures', route: 'gestures'},
{name: 'Grid List', route: 'grid-list'},
Expand Down
2 changes: 2 additions & 0 deletions src/demo-app/demo-app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ import {PlatformDemo} from '../platform/platform-demo';
import {AutocompleteDemo} from '../autocomplete/autocomplete-demo';
import {InputDemo} from '../input/input-demo';
import {StyleDemo} from '../style/style-demo';
import {DataTableDemo} from '../data-table/data-table-demo';

export const DEMO_APP_ROUTES: Routes = [
{path: '', component: Home},
{path: 'autocomplete', component: AutocompleteDemo},
{path: 'button', component: ButtonDemo},
{path: 'card', component: CardDemo},
{path: 'chips', component: ChipsDemo},
{path: 'data-table', component: DataTableDemo},
{path: 'radio', component: RadioDemo},
{path: 'select', component: SelectDemo},
{path: 'sidenav', component: SidenavDemo},
Expand Down
10 changes: 10 additions & 0 deletions src/lib/data-table/data-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {Observable} from 'rxjs/Observable';

export interface MdTableRows<T> {
rows: T[];
rowCount: number;
}

export interface MdTableDataSource<T> {
getRows(): Observable<MdTableRows<T>>;
}
11 changes: 11 additions & 0 deletions src/lib/data-table/data-table-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {MdError} from '../core/errors/error';

/**
* Exception thrown when a tooltip has an invalid position.
* @docs-private
*/
export class MdTableInvalidDataSourceError extends MdError {
constructor() {
super('MdDataTable: No dataSource provided.');
}
}
13 changes: 13 additions & 0 deletions src/lib/data-table/data-table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<ng-content select="md-header-row"></ng-content>
<template *ngFor="let row of rows; let index = index; let first = first;
let even = even; let odd = odd;"
[ngTemplateOutlet]="getTemplateForRow(row, index, first, even, odd)"
[ngOutletContext]="{
row: row,
index: index,
first: first,
last: isLast(index),
even: even,
odd: odd
}">
</template>
51 changes: 51 additions & 0 deletions src/lib/data-table/data-table.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
$table-border-color: #000;

.mat-table {
display: block;
position: relative;
}

.mat-table-row,
.mat-table-header-row {
display: flex;
width: 100%;
}

.mat-table-cell,
.mat-table-header-cell {
display: flex;
height: 48px;
justify-content: flex-start;
align-items: center;
flex: 1;
}

.mat-table-header-cell {
font-weight: bold;

&.mat-table-sortable {
cursor: pointer;
}

@mixin mat-table-sort-arrow($direction) {
&::after {
content: '\\25b2';
font-size: small;

@if $direction == 'ascending' {
padding-right: 4px;
transform: rotate(180deg);
} @else {
padding-left: 4px;
}
}
}

&.mat-table-sort-descending {
@include mat-table-sort-arrow('descending');
}

&.mat-table-sort-ascending {
@include mat-table-sort-arrow('ascending');
}
}
Loading

0 comments on commit d1e8ad9

Please sign in to comment.