tabindex=1 so the grid body can capture keyboard events.
-->
<div class="eg-grid-body" tabindex="1" (keydown)="onGridKeyDown($event)">
- <div class="eg-grid-row eg-grid-body-row {{context.rowClassCallback(row)}}"
+ <div role="row" class="eg-grid-row eg-grid-body-row {{context.rowClassCallback(row)}}"
[ngClass]="{'selected': context.rowSelector.contains(context.getRowIndex(row))}"
*ngFor="let row of context.dataSource.getPageOfRows(context.pager); let idx = index">
</ng-container>
</ng-container>
</div>
- <div class="eg-grid-cell eg-grid-body-cell" [ngStyle]="{flex:col.flex}"
+ <div role="gridcell" class="eg-grid-cell eg-grid-body-cell"
+ [ngStyle]="{flex:col.flex}"
[ngClass]="{'eg-grid-cell-overflow': context.overflowCells}"
(dblclick)="onRowDblClick(row)"
(click)="onRowClick($event, row, idx)"
onGridKeyDown(evt: KeyboardEvent) {
switch (evt.key) {
case 'ArrowUp':
- this.context.selectPreviousRow();
+ if (evt.shiftKey) {
+ // Extend selection up one row
+ this.context.selectMultiRowsPrevious();
+ } else {
+ this.context.selectPreviousRow();
+ }
evt.stopPropagation();
break;
case 'ArrowDown':
- this.context.selectNextRow();
+ if (evt.shiftKey) {
+ // Extend selection down one row
+ this.context.selectMultiRowsNext();
+ } else {
+ this.context.selectNextRow();
+ }
evt.stopPropagation();
break;
case 'ArrowLeft':
+ case 'PageUp':
this.context.toPrevPage()
.then(ok => this.context.selectFirstRow(), err => {});
evt.stopPropagation();
break;
case 'ArrowRight':
+ case 'PageDown':
this.context.toNextPage()
.then(ok => this.context.selectFirstRow(), err => {});
evt.stopPropagation();
break;
+ case 'a':
+ // control-a means select all visible rows.
+ // For consistency, select all rows in the current page only.
+ if (evt.ctrlKey) {
+ this.context.rowSelector.clear();
+ this.context.selectRowsInPage();
+ evt.preventDefault();
+ }
+ break;
+
case 'Enter':
if (this.context.lastSelectedIndex) {
this.grid.onRowActivate.emit(
-<div class="eg-grid-row eg-grid-header-row">
+<div row="row" class="eg-grid-row eg-grid-header-row">
<ng-container *ngIf="!context.disableSelect">
- <div class="eg-grid-cell eg-grid-header-cell eg-grid-checkbox-cell eg-grid-cell-skinny">
- <input type='checkbox' (click)="handleBatchSelect($event)">
+ <div role="columnheader"
+ class="eg-grid-cell eg-grid-header-cell eg-grid-checkbox-cell eg-grid-cell-skinny">
+ <input type='checkbox' (click)="handleBatchSelect($event)"
+ [(ngModel)]="batchRowCheckbox">
</div>
</ng-container>
- <div class="eg-grid-cell eg-grid-header-cell eg-grid-number-cell eg-grid-cell-skinny">
+ <div role="columnheader"
+ class="eg-grid-cell eg-grid-header-cell eg-grid-number-cell eg-grid-cell-skinny">
<span i18n="number|Row Number Header">#</span>
</div>
- <div *ngIf="context.rowFlairIsEnabled"
+ <div *ngIf="context.rowFlairIsEnabled"
+ role="columnheader"
class="eg-grid-cell eg-grid-header-cell eg-grid-flair-cell">
<span class="material-icons">notifications</span>
</div>
- <div *ngFor="let col of context.columnSet.displayColumns()"
- draggable="true"
+ <div role="columnheader"
+ *ngFor="let col of context.columnSet.displayColumns()"
+ draggable="true"
(dragstart)="dragColumn = col"
(drop)="onColumnDrop(col)"
(dragover)="onColumnDragEnter($event, col)"
(dragleave)="onColumnDragLeave($event, col)"
[ngClass]="{'dragover' : col.isDragTarget}"
class="eg-grid-cell eg-grid-header-cell" [ngStyle]="{flex:col.flex}">
- <a class="sortable label-with-material-icon" *ngIf="col.isSortable"
+ <a class="sortable label-with-material-icon" *ngIf="col.isSortable"
(click)="sortOneColumn(col)">
<span class="eg-grid-header-cell-sort-label">{{col.label}}</span>
<span class="material-icons eg-grid-header-cell-sort-arrow"
dragColumn: GridColumn;
+ batchRowCheckbox: boolean;
+
constructor() {}
- ngOnInit() {}
+ ngOnInit() {
+ this.context.selectRowsInPageEmitter.subscribe(
+ () => this.batchRowCheckbox = true
+ );
+ }
onColumnDragEnter($event: any, col: any) {
if (this.dragColumn && this.dragColumn.name !== col.name) {
}
selectAll() {
- const rows = this.context.dataSource.getPageOfRows(this.context.pager);
- const indexes = rows.map(r => this.context.getRowIndex(r));
- this.context.rowSelector.select(indexes);
+ this.context.selectRowsInPage();
}
allRowsAreSelected(): boolean {
<label class="form-check-label">
<input class="form-check-input" type="checkbox"
[(ngModel)]="cb.isChecked"
- (click)="cb.onChange.emit($event.target.checked)"/>
+ (click)="cb.onChange($event.target.checked)"/>
{{cb.label}}
</label>
</ng-container>
<!-- push everything else to the right -->
<div class="flex-1"></div>
+ <div class="font-sm font-italic d-flex flex-column-reverse mr-2">
+ {{gridContext.rowSelector.selected().length}} selected
+ </div>
<div ngbDropdown class="mr-1" placement="bottom-right">
<button ngbDropdownToggle [disabled]="!gridContext.toolbarActions.length"
class="btn btn-outline-dark no-dropdown-caret">
-<div class="eg-grid">
+<div class="eg-grid" role="grid">
<eg-grid-toolbar
- [gridContext]="context"
+ [gridContext]="context"
[gridPrinter]="gridPrinter"
[colWidthConfig]="colWidthConfig">
</eg-grid-toolbar>
<eg-grid-column-width #colWidthConfig [gridContext]="context">
</eg-grid-column-width>
-
+
<eg-grid-print #gridPrinter [gridContext]="context">
</eg-grid-print>
overflowCells: boolean;
showLinkSelectors: boolean;
+ // Allow calling code to know when the select-all-rows-in-page
+ // action has occurred.
+ selectRowsInPageEmitter: EventEmitter<void>;
+
// Services injected by our grid component
idl: IdlService;
org: OrgService;
}
init() {
+ this.selectRowsInPageEmitter = new EventEmitter<void>();
this.columnSet = new GridColumnSet(this.idl, this.idlClass);
this.columnSet.isSortable = this.isSortable === true;
this.columnSet.isMultiSortable = this.isMultiSortable === true;
this.lastSelectedIndex = index;
}
+ selectMultipleRows(indexes: any[]) {
+ this.rowSelector.clear();
+ this.rowSelector.select(indexes);
+ this.lastSelectedIndex = indexes[indexes.length - 1];
+ }
+
// selects or deselects an item, without affecting the others.
// returns true if the item is selected; false if de-selected.
toggleSelectOneRow(index: any) {
}
}
+ // shift-up-arrow
+ // Select the previous row in addition to any currently selected row.
+ // However, if the previous row is already selected, assume the user
+ // has reversed direction and now wants to de-select the last selected row.
+ selectMultiRowsPrevious() {
+ if (!this.lastSelectedIndex) { return; }
+ const pos = this.getRowPosition(this.lastSelectedIndex);
+ const selectedIndexes = this.rowSelector.selected();
+
+ const promise = // load the previous page of data if needed
+ (pos === this.pager.offset) ? this.toPrevPage() : Promise.resolve();
+
+ promise.then(
+ ok => {
+ const row = this.dataSource.data[pos - 1];
+ const newIndex = this.getRowIndex(row);
+ if (selectedIndexes.filter(i => i === newIndex).length > 0) {
+ // Prev row is already selected. User is reversing direction.
+ this.rowSelector.deselect(this.lastSelectedIndex);
+ this.lastSelectedIndex = newIndex;
+ } else {
+ this.selectMultipleRows(selectedIndexes.concat(newIndex));
+ }
+ },
+ err => {}
+ );
+ }
+
+ // shift-down-arrow
+ // Select the next row in addition to any currently selected row.
+ // However, if the next row is already selected, assume the user
+ // has reversed direction and wants to de-select the last selected row.
+ selectMultiRowsNext() {
+ if (!this.lastSelectedIndex) { return; }
+ const pos = this.getRowPosition(this.lastSelectedIndex);
+ const selectedIndexes = this.rowSelector.selected();
+
+ const promise = // load the next page of data if needed
+ (pos === (this.pager.offset + this.pager.limit - 1)) ?
+ this.toNextPage() : Promise.resolve();
+
+ promise.then(
+ ok => {
+ const row = this.dataSource.data[pos + 1];
+ const newIndex = this.getRowIndex(row);
+ if (selectedIndexes.filter(i => i === newIndex).length > 0) {
+ // Next row is already selected. User is reversing direction.
+ this.rowSelector.deselect(this.lastSelectedIndex);
+ this.lastSelectedIndex = newIndex;
+ } else {
+ this.selectMultipleRows(selectedIndexes.concat(newIndex));
+ }
+ },
+ err => {}
+ );
+ }
+
+ getFirstRowInPage(): any {
+ return this.dataSource.data[this.pager.offset];
+ }
+
+ getLastRowInPage(): any {
+ return this.dataSource.data[this.pager.offset + this.pager.limit - 1];
+ }
+
selectFirstRow() {
- this.selectRowByPos(this.pager.offset);
+ this.selectOneRow(this.getRowIndex(this.getFirstRowInPage()));
}
selectLastRow() {
- this.selectRowByPos(this.pager.offset + this.pager.limit - 1);
+ this.selectOneRow(this.getRowIndex(this.getLastRowInPage()));
+ }
+
+ selectRowsInPage() {
+ const rows = this.dataSource.getPageOfRows(this.pager);
+ const indexes = rows.map(r => this.getRowIndex(r));
+ this.rowSelector.select(indexes);
+ this.selectRowsInPageEmitter.emit();
}
toPrevPage(): Promise<any> {