Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit 14ed60e

Browse files
thelgevoldFoxandxss
authored andcommitted
docs:(created virtual grid cookbook)
docs: update component name to have Component suffix + update display value for path in code samples data.json rc img new image core lint update syntax directive
1 parent 5fd6ae3 commit 14ed60e

File tree

19 files changed

+560
-0
lines changed

19 files changed

+560
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/// <reference path='../_protractor/e2e.d.ts' />
2+
'use strict';
3+
4+
describe('Virtual Grid', function () {
5+
6+
beforeEach(function () {
7+
browser.get('');
8+
});
9+
10+
it('should sort the grid', function() {
11+
ValidateRankingColumn(['1000', '999', '998', '997', '996', '995', '994', '993', '992', '991', '990']);
12+
13+
// click Ranking column and make sure values are sorted
14+
element(by.xpath('//tbody/tr[1]/td[3]')).click().then(function(){
15+
ValidateRankingColumn(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']);
16+
});
17+
});
18+
19+
function ValidateRankingColumn(range: string[]) {
20+
for (let i = 0; i < range.length; i++) {
21+
let rankCell = element.all(by.xpath('//tbody/tr[' + (i + 2) + ']/td[3]//input')).get(0);
22+
expect(rankCell.getAttribute('value')).toEqual(range[i]);
23+
}
24+
}
25+
26+
it('should bind to new row but keep DOM constant', function () {
27+
28+
let lastRowFirstTextCell = element(by.css('#cell10-0'));
29+
30+
// check that the last row number in the grid is 10
31+
expect(element(by.xpath('//tbody/tr[12]/td[1][contains(text(),10)]')).isPresent()).toBe(true);
32+
33+
let trs = element.all(by.xpath('//tbody/tr'));
34+
expect(trs.count()).toBe(12);
35+
36+
lastRowFirstTextCell.click().then(function(){
37+
lastRowFirstTextCell.clear();
38+
lastRowFirstTextCell.sendKeys('New Hero');
39+
expect(lastRowFirstTextCell.getAttribute('value')).toEqual('New Hero');
40+
41+
// page down one row
42+
lastRowFirstTextCell.sendKeys(protractor.Key.ARROW_DOWN);
43+
// navigating does not increase the number of rows
44+
trs = element.all(by.xpath('//tbody/tr'));
45+
expect(trs.count()).toBe(12);
46+
});
47+
});
48+
});
49+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/*.js
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component } from '@angular/core';
2+
3+
import { HeroGridComponent } from './hero-grid.component';
4+
5+
@Component({
6+
selector: 'my-app',
7+
template: '<hero-grid></hero-grid>',
8+
directives: [HeroGridComponent]
9+
})
10+
11+
export class AppComponent {
12+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// #docregion
2+
import { Component, Input, Output, EventEmitter, ElementRef, Renderer, ViewChild } from '@angular/core';
3+
4+
import { Column } from './column';
5+
import { HeroGridService } from './hero-grid.service';
6+
import { KeyCodeService } from './key-code.service';
7+
8+
@Component({
9+
selector: 'grid-cell',
10+
template: `<input #input
11+
[value]="col.cellValue"
12+
[id]="id"
13+
(input)="col.cellValue = $event.target.value"
14+
(click)="heroGridService.selectColumn(col)" (keydown)="onKeyDown($event)" />`
15+
})
16+
17+
export class CellComponent {
18+
@Input() col: Column;
19+
@Input() id: string;
20+
@Output() navigate = new EventEmitter();
21+
@ViewChild('input') input: ElementRef;
22+
23+
constructor(public heroGridService: HeroGridService, private elementRef: ElementRef,
24+
private renderer: Renderer, private keyCodeService: KeyCodeService) {
25+
}
26+
27+
select() {
28+
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
29+
}
30+
31+
onKeyDown(e: any) {
32+
let key = this.keyCodeService.getNavigationKey(e.keyCode);
33+
if (key.isArrowKey) {
34+
this.navigate.emit(e);
35+
}
36+
return !key.tab;
37+
}
38+
}
39+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// #docregion
2+
import { Row } from './row';
3+
4+
export class Column {
5+
cellValue: string;
6+
row: Row;
7+
8+
constructor(public columnIndex: number, row: Row) {
9+
this.cellValue = '';
10+
this.row = row;
11+
}
12+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// #docregion
2+
import { Row } from './row';
3+
4+
export class HeroDataService {
5+
6+
getApplicants(count: number): Array<Row> {
7+
8+
let rows: Array<Row> = [];
9+
let heroes: Array<string> = ['Mr. Nice',
10+
'Narco',
11+
'Bombasto',
12+
'Celeritas',
13+
'Magneta',
14+
'RubberMan',
15+
'Dynama',
16+
'Dr IQ',
17+
'Magma',
18+
'Tornado'];
19+
20+
for (let i = 0; i < count; i++) {
21+
let heroIndex = this.generateRandomNumber(heroes.length - 1);
22+
let heroData = [heroes[heroIndex], count - i, this.generateRandomNumber(70) + 30];
23+
rows.push(this.createCell(i, heroData));
24+
}
25+
return rows;
26+
}
27+
28+
private generateRandomNumber(upperBound: number) {
29+
return Math.floor(Math.random() * upperBound);
30+
}
31+
32+
private createCell(index: number, values: Array<any>): Row {
33+
let row = new Row(values.length);
34+
35+
for (let i = 0; i < values.length; i++) {
36+
row.columns[i].cellValue = values[i];
37+
}
38+
return row;
39+
}
40+
}
41+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// #docregion
2+
import { Injectable } from '@angular/core';
3+
4+
import { Row } from './row';
5+
6+
@Injectable()
7+
export class HeroGridSortingService {
8+
sortDirection: number = 1;
9+
10+
sort(rows: Array<Row>, colIndex: number) {
11+
this.sortDirection *= -1;
12+
rows.sort((a, b) => {
13+
if (a.columns[colIndex].cellValue === b.columns[colIndex].cellValue) {
14+
return 0;
15+
}
16+
17+
if (a.columns[colIndex].cellValue > b.columns[colIndex].cellValue) {
18+
return -1 * this.sortDirection;
19+
}
20+
21+
if (a.columns[colIndex].cellValue < b.columns[colIndex].cellValue) {
22+
return 1 * this.sortDirection;
23+
}
24+
});
25+
}
26+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// #docregion
2+
import { Component, AfterViewChecked, ViewChildren, QueryList } from '@angular/core';
3+
4+
import { CellComponent } from './cell.component';
5+
import { HeroGridSortingService } from './hero-grid-sorting.service';
6+
import { HeroGridService } from './hero-grid.service';
7+
import { KeyCodeService } from './key-code.service';
8+
import { HeroDataService } from './hero-data.service';
9+
import { Row } from './row';
10+
11+
@Component({
12+
selector: 'hero-grid',
13+
directives: [CellComponent],
14+
providers: [HeroGridService, KeyCodeService, HeroDataService, HeroGridSortingService],
15+
template: `<h1>Hero Grid</h1>
16+
<table id="hero-grid">
17+
<tr>
18+
<td class="row-number-column"></td>
19+
<td (click)="sort(colIndex)" class="columnHeader"
20+
*ngFor="let columnHeader of heroGridService.header; let colIndex = index">
21+
{{columnHeader}}
22+
</td>
23+
</tr>
24+
<tr *ngFor="let row of visibleRows">
25+
<td class="row-number-column">
26+
{{heroGridService.rows.indexOf(row)}}
27+
</td>
28+
<td *ngFor="let col of row.columns">
29+
<grid-cell [id]="heroGridService.createCellSelector(row, col)"
30+
[col]="col"
31+
(navigate)="navigate($event)">
32+
</grid-cell>
33+
</td>
34+
</tr>
35+
</table>`
36+
})
37+
38+
export class HeroGridComponent implements AfterViewChecked {
39+
40+
visibleRows: Array<Row> = [];
41+
heroGridService: HeroGridService;
42+
@ViewChildren(CellComponent) cells: QueryList<CellComponent>;
43+
44+
constructor(heroGridService: HeroGridService) {
45+
this.heroGridService = heroGridService;
46+
this.visibleRows = this.heroGridService.getVisibleRows();
47+
}
48+
49+
navigate($event: any) {
50+
this.heroGridService.navigate($event.keyCode);
51+
this.visibleRows = this.heroGridService.getVisibleRows();
52+
}
53+
54+
sort(columnIndex: number) {
55+
this.heroGridService.sort(columnIndex);
56+
this.visibleRows = this.heroGridService.getVisibleRows();
57+
}
58+
59+
ngAfterViewChecked() {
60+
let id = this.heroGridService.getCurrentCellSelector();
61+
let currentCell = this.cells.toArray().find(cell => cell.id === id);
62+
currentCell.select();
63+
}
64+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// #docregion
2+
import { Injectable } from '@angular/core';
3+
4+
import { Column } from './column';
5+
import { HeroDataService } from './hero-data.service';
6+
import { HeroGridSortingService } from './hero-grid-sorting.service';
7+
import { KeyCodeService } from './key-code.service';
8+
import { Row } from './row';
9+
10+
@Injectable()
11+
export class HeroGridService {
12+
static maxRows = 1000;
13+
header: Array<string> = ['Name', 'Ranking', 'Age'];
14+
rows: Array<Row>;
15+
currentColumn: Column;
16+
currentRowIndex: number = 0;
17+
18+
private gridWindow: any;
19+
20+
keyCodeService: KeyCodeService;
21+
sortingService: HeroGridSortingService;
22+
23+
constructor(keyCodeService: KeyCodeService, heroDataService: HeroDataService, sortingService: HeroGridSortingService) {
24+
this.keyCodeService = keyCodeService;
25+
this.rows = heroDataService.getApplicants(HeroGridService.maxRows);
26+
this.sortingService = sortingService;
27+
28+
this.init();
29+
30+
let missingRows = this.gridWindow.pageSize - this.rows.length;
31+
32+
for (let i = 0; i <= missingRows; i++) {
33+
this.rows.push(new Row(this.header.length));
34+
}
35+
}
36+
37+
selectColumn(col: Column) {
38+
this.currentColumn = col;
39+
this.currentRowIndex = this.rows.indexOf(this.currentColumn.row);
40+
}
41+
42+
sort(colIndex: number) {
43+
this.sortingService.sort(this.rows, colIndex);
44+
this.init();
45+
}
46+
47+
createCellSelector(row: Row, col: Column): string {
48+
return 'cell' + this.rows.indexOf(row) + '-' + row.columns.indexOf(col);
49+
}
50+
51+
getCurrentCellSelector() {
52+
let cellIndex = this.rows[this.currentRowIndex].columns.indexOf(this.currentColumn);
53+
return 'cell' + this.currentRowIndex + '-' + cellIndex;
54+
}
55+
56+
getVisibleRows() {
57+
let visible: Array<Row> = [];
58+
for (let i = this.gridWindow.start; i <= this.gridWindow.end; i++) {
59+
visible.push(this.rows[i]);
60+
}
61+
return visible;
62+
}
63+
64+
navigate(keyCode: number) {
65+
let navDirection = this.keyCodeService.getNavigationKey(keyCode);
66+
67+
if (navDirection.down) {
68+
this.ensureRow();
69+
this.currentColumn = this.rows[this.currentRowIndex + 1].columns[this.currentColumn.columnIndex];
70+
this.adjustRowRangeDownward();
71+
}
72+
73+
if (navDirection.up) {
74+
if (this.currentRowIndex > 0) {
75+
this.currentColumn = this.rows[this.currentRowIndex - 1].columns[this.currentColumn.columnIndex];
76+
this.adjustRowRangeUpward();
77+
}
78+
}
79+
80+
if (navDirection.left) {
81+
if (this.currentColumn.columnIndex > 0) {
82+
this.currentColumn = this.rows[this.currentRowIndex].columns[this.currentColumn.columnIndex - 1];
83+
}
84+
}
85+
86+
if (navDirection.right) {
87+
if (this.currentColumn.columnIndex < this.header.length - 1) {
88+
this.currentColumn = this.rows[this.currentRowIndex].columns[this.currentColumn.columnIndex + 1];
89+
}
90+
}
91+
92+
this.currentRowIndex = this.rows.indexOf(this.currentColumn.row);
93+
}
94+
95+
private adjustRowRangeUpward() {
96+
if (this.currentRowIndex <= this.gridWindow.start) {
97+
this.shiftRowsBy(-1);
98+
}
99+
}
100+
101+
private adjustRowRangeDownward() {
102+
if (this.currentRowIndex === this.gridWindow.end) {
103+
this.shiftRowsBy(1);
104+
}
105+
}
106+
107+
private shiftRowsBy(offset: number) {
108+
this.gridWindow.start = this.gridWindow.start + offset;
109+
this.gridWindow.end = this.gridWindow.end + offset;
110+
}
111+
112+
private ensureRow() {
113+
if (this.currentRowIndex + 1 >= this.rows.length) {
114+
this.rows.push(new Row(this.header.length));
115+
}
116+
}
117+
118+
private init() {
119+
this.gridWindow = {pageSize: 10, start: 0, end: 10};
120+
this.currentColumn = this.rows[0].columns[0];
121+
this.currentRowIndex = 0;
122+
}
123+
}
124+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// #docregion
2+
export class KeyCodeService {
3+
4+
getNavigationKey(keyCode: number): any {
5+
let key: any = {
6+
up: keyCode === 38,
7+
down: keyCode === 40,
8+
right: keyCode === 39,
9+
left: keyCode === 37,
10+
tab: keyCode === 9,
11+
};
12+
key.isArrowKey = key.up || key.down || key.right || key.left;
13+
14+
return key;
15+
}
16+
}
17+

0 commit comments

Comments
 (0)