-import {Component, Input, ViewChild, TemplateRef} from '@angular/core';
+import {Component, Input, OnInit} from '@angular/core';
import {EgDialogComponent} from '@eg/share/dialog/dialog.component';
-import {EgGridService, EgGridColumn, EgGridColumnSet} from './grid.service';
+import {EgGridColumnSet} from './grid';
@Component({
selector: 'eg-grid-column-config',
/**
*/
-export class EgGridColumnConfigComponent extends EgDialogComponent {
+export class EgGridColumnConfigComponent extends EgDialogComponent implements OnInit {
@Input() columnSet: EgGridColumnSet;
-
}
import {Component, Input, OnInit, Host} from '@angular/core';
-import {EgGridService, EgGridColumn, EgGridColumnSet} from './grid.service';
-import {EgGridDataSource} from './grid-data-source';
-import {EgGridComponent} from './grid.component';
+import {EgGridContext, EgGridColumn, EgGridColumnSet,
+ EgGridDataSource} from './grid';
@Component({
selector: 'eg-grid-column-width',
export class EgGridColumnWidthComponent implements OnInit {
- @Input() columnSet: EgGridColumnSet;
+ @Input() gridContext: EgGridContext;
+ columnSet: EgGridColumnSet;
isVisible: boolean;
- constructor(
- private gridSvc: EgGridService,
- @Host() private grid: EgGridComponent
- ) { }
+ constructor() {}
ngOnInit() {
this.isVisible = false;
+ this.columnSet = this.gridContext.columnSet;
}
expandColumn(col: EgGridColumn) {
import {Component, Input, OnInit, Host, TemplateRef} from '@angular/core';
-import {EgGridService, EgGridColumn, EgGridColumnSet} from './grid.service';
+import {EgGridColumn, EgGridColumnSet} from './grid';
import {EgGridComponent} from './grid.component';
@Component({
@Input() cellTemplate: TemplateRef<any>;
// get a reference to our container grid.
- constructor(
- private gridSvc: EgGridService,
- @Host() private grid: EgGridComponent) {}
+ constructor(@Host() private grid: EgGridComponent) {}
ngOnInit() {
col.cellTemplate = this.cellTemplate;
col.isSortable = this.sortable;
col.isMultiSortable = this.multiSortable;
- this.grid.columnSet.add(col);
+ this.grid.context.columnSet.add(col);
}
}
+++ /dev/null
-import {EventEmitter} from '@angular/core';
-import {Observable} from 'rxjs/Rx';
-import {Pager} from '@eg/share/util/pager';
-
-export class EgGridDataSource {
-
- data: any[];
- sort: any[];
- pager: Pager;
- allRowsRetrieved: boolean;
- getRows: (pager: Pager, sort: any[]) => Observable<any>;
-
- constructor() {
- this.sort = [];
- this.reset();
- }
-
- reset() {
- this.data = [];
- this.allRowsRetrieved = false;
- if (this.pager) this.pager.resultCount = null;
- }
-
- setAllRetrieved() {
- this.allRowsRetrieved = true;
- this.pager.resultCount = this.data.length;
- }
-
- // called from the template -- no data fetching
- getPageOfRows(pager: Pager): any[] {
- if (this.data) {
- return this.data.slice(
- pager.offset, pager.limit + pager.offset);
- }
- return [];
- }
-
- // called on initial component load and user action (e.g. paging, sorting).
- requestPage(pager: Pager): Promise<any> {
-
- if (
- // already have the current page
- this.getPageOfRows(pager).length > 0
- // already have all data
- || this.allRowsRetrieved
- // have no way to get more data.
- || !this.getRows
- ) {
- return Promise.resolve();
- }
-
- return new Promise((resolve, reject) => {
- let idx = pager.offset;
- return this.getRows(pager, this.sort).subscribe(
- row => this.data[idx++] = row,
- err => {
- console.error(`grid getRows() error ${err}`);
- reject(err);
- },
- () => {
- this.checkAllRetrieved(pager, idx);
- resolve();
- }
- );
- });
- }
-
- // See if the last getRows() call resulted in the final set of data.
- checkAllRetrieved(pager: Pager, idx: number) {
- if (this.allRowsRetrieved) return;
-
- if (idx == 0 || idx < (pager.limit + pager.offset)) {
- // last query returned nothing or less than one page.
- // confirm we have all of the preceding pages.
- if (!this.data.includes(undefined)) {
- this.allRowsRetrieved = true;
- pager.resultCount = this.data.length;
- }
- }
- }
-}
-
-
<div 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 *ngFor="let col of columnSet.displayColumns()"
+ <div *ngFor="let col of gridContext.columnSet.displayColumns()"
draggable="true"
(dragstart)="dragColumn = col"
(drop)="onColumnDrop(col)"
-import {Component, Input, OnInit, Host} from '@angular/core';
-import {EgGridService, EgGridColumn,
- EgGridRowSelector, EgGridColumnSet} from './grid.service';
-import {EgGridDataSource} from './grid-data-source';
-import {EgGridComponent} from './grid.component';
-import {Pager} from '@eg/share/util/pager';
+import {Component, Input, OnInit} from '@angular/core';
+import {EgGridContext, EgGridColumn, EgGridRowSelector,
+ EgGridColumnSet, EgGridDataSource} from './grid';
@Component({
selector: 'eg-grid-header',
export class EgGridHeaderComponent implements OnInit {
- @Input() columnSet: EgGridColumnSet;
- @Input() rowSelector: EgGridRowSelector;
- @Input() dataSource: EgGridDataSource;
- @Input() pager: Pager;
+ @Input() gridContext: EgGridContext;
dragColumn: EgGridColumn;
- constructor(
- private gridSvc: EgGridService,
- @Host() private grid: EgGridComponent
- ) { }
+ constructor() {}
- ngOnInit() {
- }
+ ngOnInit() {}
onColumnDragEnter($event: any, col: any) {
if (this.dragColumn && this.dragColumn.name != col.name)
}
onColumnDrop(col: EgGridColumn) {
- this.columnSet.insertBefore(this.dragColumn, col);
- this.columnSet.columns.forEach(c => c.isDragTarget = false);
+ this.gridContext.columnSet.insertBefore(this.dragColumn, col);
+ this.gridContext.columnSet.columns.forEach(c => c.isDragTarget = false);
}
sortOneColumn(col: EgGridColumn) {
let dir = 'ASC';
- let sort = this.dataSource.sort;
+ let sort = this.gridContext.dataSource.sort;
if (sort.length && sort[0].name == col.name && sort[0].dir == 'ASC') {
dir = 'DESC';
}
- this.dataSource.sort = [{name: col.name, dir: dir}];
- this.grid.reload();
+ this.gridContext.dataSource.sort = [{name: col.name, dir: dir}];
+ this.gridContext.reload();
}
// Returns true if the provided column is sorting in the
// specified direction.
isColumnSorting(col: EgGridColumn, dir: string): boolean {
- let sort = this.dataSource.sort.filter(c => c.name == col.name)[0];
+ let sort = this.gridContext.dataSource.sort.filter(c => c.name == col.name)[0];
return sort && sort.dir == dir;
}
handleBatchSelect($event) {
if ($event.target.checked) {
- if (this.rowSelector.isEmpty() || !this.allRowsAreSelected()) {
+ if (this.gridContext.rowSelector.isEmpty() || !this.allRowsAreSelected()) {
// clear selections from other pages to avoid confusion.
- this.rowSelector.clear();
+ this.gridContext.rowSelector.clear();
this.selectAll();
}
} else {
- this.rowSelector.clear();
+ this.gridContext.rowSelector.clear();
}
}
selectAll() {
- let rows = this.dataSource.getPageOfRows(this.pager);
- let indexes = rows.map(r => this.grid.getRowIndex(r));
- this.rowSelector.select(indexes);
+ let rows = this.gridContext.dataSource.getPageOfRows(this.gridContext.pager);
+ let indexes = rows.map(r => this.gridContext.getRowIndex(r));
+ this.gridContext.rowSelector.select(indexes);
}
allRowsAreSelected(): boolean {
- let rows = this.dataSource.getPageOfRows(this.pager);
- let indexes = rows.map(r => this.grid.getRowIndex(r));
- return this.rowSelector.contains(indexes);
+ let rows = this.gridContext.dataSource.getPageOfRows(this.gridContext.pager);
+ let indexes = rows.map(r => this.gridContext.getRowIndex(r));
+ return this.gridContext.rowSelector.contains(indexes);
}
}
import {Component, Input, OnInit, Host, TemplateRef} from '@angular/core';
-import {EgGridService, EgGridToolbarAction} from './grid.service';
+import {EgGridToolbarAction} from './grid';
import {EgGridComponent} from './grid.component';
@Component({
@Input() action: (rows: any[]) => any;
// get a reference to our container grid.
- constructor(
- private gridSvc: EgGridService,
- @Host() private grid: EgGridComponent) {
- }
+ constructor(@Host() private grid: EgGridComponent) {}
ngOnInit() {
action.label = this.label;
action.action = this.action;
- this.grid.toolbarActions.push(action);
+ this.grid.context.toolbarActions.push(action);
}
}
import {Component, Input, OnInit, Host, TemplateRef} from '@angular/core';
-import {EgGridService, EgGridToolbarButton} from './grid.service';
+import {EgGridToolbarButton} from './grid';
import {EgGridComponent} from './grid.component';
@Component({
@Input() action: () => any;
// get a reference to our container grid.
- constructor(
- private gridSvc: EgGridService,
- @Host() private grid: EgGridComponent) {
- }
+ constructor(@Host() private grid: EgGridComponent) {}
ngOnInit() {
btn.label = this.label;
btn.action = this.action;
- this.grid.toolbarButtons.push(btn);
+ this.grid.context.toolbarButtons.push(btn);
}
}
<div class="eg-grid-toolbar mb-2">
- <div class="btn-toolbar" *ngIf="toolbarButtons.length">
+ <div class="btn-toolbar" *ngIf="gridContext.toolbarButtons.length">
<div class="btn-grp">
- <button *ngFor="let btn of toolbarButtons" class="btn btn-outline-dark" (click)="btn.action()">
+ <button *ngFor="let btn of gridContext.toolbarButtons"
+ class="btn btn-outline-dark" (click)="btn.action()">
{{btn.label}}
</button>
</div>
<div class="flex-1"></div>
<div ngbDropdown class="ml-1" placement="bottom-right">
- <button ngbDropdownToggle [disabled]="!toolbarActions.length"
+ <button ngbDropdownToggle [disabled]="!gridContext.toolbarActions.length"
class="btn btn-outline-dark no-dropdown-caret">
<span title="Actions For Selected Rows" i18n-title
class="material-icons mat-icon-in-button">playlist_add_check</span>
</button>
<div class="dropdown-menu" ngbDropdownMenu>
<a class="dropdown-item" (click)="performAction(action)"
- *ngFor="let action of toolbarActions">
+ *ngFor="let action of gridContext.toolbarActions">
<span class="ml-2">{{action.label}}</span>
</a>
</div>
<div class="btn-toolbar ml-1">
<div class="btn-grp">
- <button [disabled]="pager.isFirstPage()" type="button"
- class="btn btn-outline-dark" (click)="pager.toFirst()">
+ <button [disabled]="gridContext.pager.isFirstPage()" type="button"
+ class="btn btn-outline-dark" (click)="gridContext.pager.toFirst()">
<span title="First Page" i18n-title
class="material-icons mat-icon-in-button">first_page</span>
</button>
- <button [disabled]="pager.isFirstPage()" type="button"
- class="btn btn-outline-dark" (click)="pager.decrement()">
+ <button [disabled]="gridContext.pager.isFirstPage()" type="button"
+ class="btn btn-outline-dark" (click)="gridContext.pager.decrement()">
<span title="Previous Page" i18n-title
class="material-icons mat-icon-in-button">keyboard_arrow_left</span>
</button>
- <button [disabled]="pager.isLastPage()" type="button"
- class="btn btn-outline-dark" (click)="pager.increment()">
+ <button [disabled]="gridContext.pager.isLastPage()" type="button"
+ class="btn btn-outline-dark" (click)="gridContext.pager.increment()">
<span title="Next Page" i18n-title
class="material-icons mat-icon-in-button">keyboard_arrow_right</span>
</button>
- <button [disabled]="!pager.resultCount || pager.isLastPage()"
- type="button" class="btn btn-outline-dark" (click)="pager.toLast()">
+ <button [disabled]="!gridContext.pager.resultCount || gridContext.pager.isLastPage()"
+ type="button" class="btn btn-outline-dark" (click)="gridContext.pager.toLast()">
<span title="First Page" i18n-title
class="material-icons mat-icon-in-button">last_page</span>
</button>
</div>
</div>
- <eg-grid-column-config #columnConfDialog [columnSet]="columnSet">
+ <eg-grid-column-config #columnConfDialog [columnSet]="gridContext.columnSet">
</eg-grid-column-config>
<div ngbDropdown class="ml-1" placement="bottom-right">
<button ngbDropdownToggle class="btn btn-outline-dark no-dropdown-caret">
<span class="ml-2" i18n>Save Columns</span>
</a>
<a class="dropdown-item label-with-material-icon"
- (click)="columnSet.reset()">
+ (click)="gridContext.columnSet.reset()">
<span class="material-icons">restore</span>
<span class="ml-2" i18n>Reset Columns</span>
</a>
+ <a class="dropdown-item label-with-material-icon"
+ (click)="downloadCsv()">
+ <span class="material-icons">cloud_download</span>
+ <span class="ml-2" i18n>Download CSV</span>
+ </a>
<div class="dropdown-divider"></div>
<a class="dropdown-item label-with-material-icon"
- (click)="col.visible=!col.visible" *ngFor="let col of columnSet.columns">
+ (click)="col.visible=!col.visible" *ngFor="let col of gridContext.columnSet.columns">
<span *ngIf="col.visible" class="badge badge-success">✓</span>
<span *ngIf="!col.visible" class="badge badge-warning">✗</span>
<span class="ml-2">{{col.label}}</span>
import {Component, Input, OnInit, Host} from '@angular/core';
-import {EgGridDataSource} from './grid-data-source';
import {Pager} from '@eg/share/util/pager';
-import {EgGridService, EgGridColumn, EgGridColumnSet, EgGridToolbarButton,
- EgGridToolbarAction} from '@eg/share/grid/grid.service';
-import {EgGridComponent} from './grid.component';
-import {EgDialogComponent} from '@eg/share/dialog/dialog.component';
+import {EgGridColumn, EgGridColumnSet, EgGridToolbarButton,
+ EgGridToolbarAction, EgGridContext, EgGridDataSource} from '@eg/share/grid/grid';
import {EgGridColumnWidthComponent} from './grid-column-width.component';
@Component({
export class EgGridToolbarComponent implements OnInit {
- @Input() dataSource: EgGridDataSource;
- @Input() pager: Pager;
- @Input() toolbarButtons: EgGridToolbarButton[];
- @Input() toolbarActions: EgGridToolbarAction[];
- @Input() columnSet: EgGridColumnSet;
+ @Input() gridContext: EgGridContext;
@Input() colWidthConfig: EgGridColumnWidthComponent;
- @Input() persistKey: string;
- constructor(private gridSvc: EgGridService,
- @Host() private grid: EgGridComponent) {
- }
+ constructor() {}
- ngOnInit() {
- }
+ ngOnInit() {}
saveColumns() {
// TODO: when server-side settings are supported, this operation
// may offer to save to user/workstation OR org unit settings
// depending on perms.
- this.gridSvc.saveColumns(this.columnSet, this.persistKey)
- .then(
+ this.gridContext.saveColumns().then(
// hide the with config after saving
ok => this.colWidthConfig.isVisible = false,
err => console.error(`Error saving columns: ${err}`)
}
performAction(action: EgGridToolbarAction) {
- action.action(this.grid.getSelectedRows());
+ action.action(this.gridContext.getSelectedRows());
}
+
+ downloadCsv() {
+ }
+
}
<div class="eg-grid">
- <eg-grid-toolbar
- [dataSource]="dataSource" [pager]="pager"
- [columnSet]="columnSet"
- [toolbarButtons]="toolbarButtons" [toolbarActions]="toolbarActions"
- [colWidthConfig]="colWidthConfig" persistKey="{{persistKey}}">
+ <eg-grid-toolbar [gridContext]="context" [colWidthConfig]="colWidthConfig">
</eg-grid-toolbar>
- <eg-grid-header [columnSet]="columnSet" [pager]="pager"
- [dataSource]="dataSource" [rowSelector]="rowSelector">
- </eg-grid-header>
- <eg-grid-column-width #colWidthConfig [columnSet]="columnSet"></eg-grid-column-width>
+
+ <eg-grid-header [gridContext]="context"></eg-grid-header>
+
+ <eg-grid-column-width #colWidthConfig [gridContext]="context">
+ </eg-grid-column-width>
<div class="row" *ngIf="dataSource.data.length == 0">
<div class="col-lg-12 text-center alert alert-light font-italic" i18n>
</div>
<div class="eg-grid-row eg-grid-body-row"
- [ngClass]="{'selected': rowSelector.contains(getRowIndex(row))}"
- *ngFor="let row of dataSource.getPageOfRows(pager); let idx = index">
+ [ngClass]="{'selected': context.rowSelector.contains(context.getRowIndex(row))}"
+ *ngFor="let row of dataSource.getPageOfRows(context.pager); let idx = index">
<div class="eg-grid-cell eg-grid-checkbox-cell eg-grid-cell-skinny">
- <input type='checkbox' [(ngModel)]="rowSelector.indexes[getRowIndex(row)]">
+ <input type='checkbox' [(ngModel)]="context.rowSelector.indexes[context.getRowIndex(row)]">
</div>
<div class="eg-grid-cell eg-grid-header-cell eg-grid-number-cell eg-grid-cell-skinny">
- {{pager.rowNumber(idx)}}
+ {{context.pager.rowNumber(idx)}}
</div>
<div class="eg-grid-cell eg-grid-body-cell" [ngStyle]="{flex:col.flex}"
(dblclick)="onRowDblClick(row)"
(click)="onRowClick($event, row, idx)"
- *ngFor="let col of columnSet.displayColumns()">
- {{getDisplayValue(row, col)}}
+ *ngFor="let col of context.columnSet.displayColumns()">
+ {{context.getRowColumnValue(row, col)}}
</div>
<div>
</div>
import {Component, Input, OnInit, AfterViewInit, EventEmitter, OnDestroy,
HostListener, ViewEncapsulation} from '@angular/core';
import {Subscription} from "rxjs/Subscription";
-import {EgGridDataSource} from './grid-data-source';
import {EgIdlService} from '@eg/core/idl.service';
import {EgOrgService} from '@eg/core/org.service';
-import {Pager} from '@eg/share/util/pager';
-import {EgGridService, EgGridColumn, EgGridColumnSet, EgGridToolbarButton,
- EgGridRowSelector, EgGridToolbarAction} from '@eg/share/grid/grid.service';
+import {EgStoreService} from '@eg/core/store.service';
+import {EgFormatService} from '@eg/share/util/format.service';
+import {EgGridContext, EgGridColumn, EgGridDataSource} from './grid';
@Component({
selector: 'eg-grid',
@Input() persistKey: string;
@Input() disableMultiSelect: boolean;
- pager: Pager;
- columnSet: EgGridColumnSet;
- rowSelector: EgGridRowSelector;
+ context: EgGridContext;
onRowActivate$: EventEmitter<any>;
onRowClick$: EventEmitter<any>;
- toolbarButtons: EgGridToolbarButton[];
- toolbarActions: EgGridToolbarAction[];
- lastSelectedIndex: any;
- pageChanges: Subscription;
- constructor(private gridSvc: EgGridService) {
- this.pager = new Pager();
- this.rowSelector = new EgGridRowSelector();
- this.pager.limit = 10; // TODO config
+ constructor(
+ private idl: EgIdlService,
+ private org: EgOrgService,
+ private store: EgStoreService,
+ private format: EgFormatService
+ ) {
+ this.context =
+ new EgGridContext(this.idl, this.org, this.store, this.format);
this.onRowActivate$ = new EventEmitter<any>();
this.onRowClick$ = new EventEmitter<any>();
- this.toolbarButtons = [];
- this.toolbarActions = [];
}
ngOnInit() {
- this.columnSet = new EgGridColumnSet(this.idlClass);
- this.columnSet.isSortable = this.isSortable === true;
- this.columnSet.isMultiSortable = this.isMultiSortable === true;
- this.gridSvc.generateColumns(this.columnSet);
+ this.context.idlClass = this.idlClass;
+ this.context.dataSource = this.dataSource;
+ this.context.persistKey = this.persistKey
+ this.context.isSortable = this.isSortable === true;
+ this.context.isMultiSortable = this.isMultiSortable === true;
+ this.context.disableMultiSelect = this.disableMultiSelect === true;
+ this.context.init();
}
- // Apply column configuation and fetch data after our child
- // components have told us what columns we have available.
ngAfterViewInit() {
- this.gridSvc.getColumnsConfig(this.persistKey)
- .then(conf => this.columnSet.applyColumnSettings(conf))
- .then(ok => this.dataSource.requestPage(this.pager))
- .then(ok => this.listenToPager())
+ this.context.initData();
}
ngOnDestroy() {
- this.dontListenToPager();
- }
-
- // Subscribe or unsubscribe to page-change events from the pager.
- listenToPager() {
- if (this.pageChanges) return;
- this.pageChanges = this.pager.onChange$.subscribe(
- val => this.dataSource.requestPage(this.pager));
- }
-
- dontListenToPager() {
- if (!this.pageChanges) return;
- this.pageChanges.unsubscribe();
- this.pageChanges = null
+ this.context.destroy();
}
// Grid keyboard navigation handlers.
@HostListener('window:keydown', ['$event']) onKeyDown(evt: KeyboardEvent) {
if (evt.key == 'ArrowUp') {
- this.selectPreviousRow();
+ this.context.selectPreviousRow();
} else if (evt.key == 'ArrowDown') {
- this.selectNextRow();
+ this.context.selectNextRow();
} else if (evt.key == 'ArrowLeft') {
- this.toPrevPage().then(ok => this.selectFirstRow(), err => {});
+ this.context.toPrevPage()
+ .then(ok => this.context.selectFirstRow(), err => {});
} else if (evt.key == 'ArrowRight') {
- this.toNextPage().then(ok => this.selectFirstRow(), err => {});
+ this.context.toNextPage()
+ .then(ok => this.context.selectFirstRow(), err => {});
} else if (evt.key == 'Enter') {
- if (this.lastSelectedIndex)
- this.onRowDblClick(this.getRowByIndex(this.lastSelectedIndex));
+ if (this.context.lastSelectedIndex) {
+ this.onRowActivate$.emit(
+ this.context.getRowByIndex(
+ this.context.lastSelectedIndex)
+ );
+ }
}
}
reload() {
- this.pager.offset = 0;
- this.dataSource.reset();
- this.dataSource.requestPage(this.pager);
- }
-
- getDisplayValue(row: any, col: EgGridColumn): string {
- return this.gridSvc.getRowColumnValue(row, col);
- }
-
- // Returns the index (AKA pkey) value for the row
- getRowIndex(row: any): any {
- return this.gridSvc.getRowIndex(row, this.columnSet);
- }
-
- // Returns the array position of the row-by-index in the
- // dataSource array.
- getRowPosition(index: any): number {
- // for-loop for early exit
- for (let idx = 0; idx < this.dataSource.data.length; idx++) {
- if (index == this.getRowIndex(this.dataSource.data[idx]))
- return idx;
- }
- }
-
- getRowByIndex(index: any): any {
- for (let idx = 0; idx < this.dataSource.data.length; idx++) {
- if (index == this.getRowIndex(this.dataSource.data[idx]))
- return this.dataSource.data[idx];
- }
- }
-
- // Returns all selected rows, regardless of whether they are
- // currently visible in the grid display.
- getSelectedRows(): any[] {
- let selected = [];
- this.rowSelector.selected().forEach(index => {
- let row = this.getRowByIndex(index);
- if (row) selected.push(row);
- });
- return selected;
- }
-
- onRowDblClick(row: any) {
- this.onRowActivate$.emit(row);
+ this.context.reload();
}
onRowClick($event: any, row: any, idx: number) {
- let index = this.getRowIndex(row);
+ let index = this.context.getRowIndex(row);
- if (this.disableMultiSelect) {
- this.selectOneRow(index);
+ if (this.context.disableMultiSelect) {
+ this.context.selectOneRow(index);
} else if ($event.ctrlKey || $event.metaKey /* mac command */) {
- if (this.toggleSelectOneRow(index))
- this.lastSelectedIndex = index;
+ if (this.context.toggleSelectOneRow(index))
+ this.context.lastSelectedIndex = index;
} else if ($event.shiftKey) {
// TODO shift range click
} else {
- this.selectOneRow(index);
+ this.context.selectOneRow(index);
}
this.onRowClick$.emit(row);
}
- selectOneRow(index: any) {
- this.rowSelector.clear();
- this.rowSelector.select(index);
- this.lastSelectedIndex = index;
- }
-
- // selects or deselects an item, without affecting the others.
- // returns true if the item is selected; false if de-selected.
- toggleSelectOneRow(index: any) {
- if (this.rowSelector.contains(index)) {
- this.rowSelector.deselect(index);
- return false;
- }
-
- this.rowSelector.select(index);
- return true;
- }
-
- selectRowByPos(pos: number) {
- let row = this.dataSource.data[pos];
- if (row) this.selectOneRow(this.getRowIndex(row));
- }
-
- selectPreviousRow() {
- if (!this.lastSelectedIndex) return;
- let pos = this.getRowPosition(this.lastSelectedIndex);
- if (pos == this.pager.offset) {
- this.toPrevPage().then(ok => this.selectLastRow(), err => {});
- } else {
- this.selectRowByPos(pos - 1);
- }
- }
-
- selectNextRow() {
- if (!this.lastSelectedIndex) return;
- let pos = this.getRowPosition(this.lastSelectedIndex);
- if (pos == (this.pager.offset + this.pager.limit - 1)) {
- this.toNextPage().then(ok => this.selectFirstRow(), err => {});
- } else {
- this.selectRowByPos(pos + 1);
- }
- }
-
- selectFirstRow() {
- this.selectRowByPos(this.pager.offset);
- }
-
- selectLastRow() {
- this.selectRowByPos(this.pager.offset + this.pager.limit - 1);
- }
-
- toPrevPage(): Promise<any> {
- if (this.pager.isFirstPage()) return Promise.reject('on first');
- // temp ignore pager events since we're calling requestPage manually.
- this.dontListenToPager();
- this.pager.decrement();
- this.listenToPager();
- return this.dataSource.requestPage(this.pager);
- }
-
- toNextPage(): Promise<any> {
- if (this.pager.isLastPage()) return Promise.reject('on last');
- // temp ignore pager events since we're calling requestPage manually.
- this.dontListenToPager();
- this.pager.increment();
- this.listenToPager();
- return this.dataSource.requestPage(this.pager);
+ onRowDblClick(row: any) {
+ this.onRowActivate$.emit(row);
}
}
import {EgGridColumnComponent} from './grid-column.component';
import {EgGridHeaderComponent} from './grid-header.component';
import {EgGridToolbarComponent} from './grid-toolbar.component';
-import {EgGridService} from './grid.service';
import {EgGridToolbarButtonComponent} from './grid-toolbar-button.component';
import {EgGridToolbarActionComponent} from './grid-toolbar-action.component';
import {EgGridColumnConfigComponent} from './grid-column-config.component';
EgGridToolbarActionComponent
],
providers: [
- EgGridService
]
})
+++ /dev/null
-import {Injectable, TemplateRef} from '@angular/core';
-import {EgIdlService, EgIdlObject} from '@eg/core/idl.service';
-import {EgOrgService} from '@eg/core/org.service';
-import {EgPcrudService} from '@eg/core/pcrud.service';
-import {EgStoreService} from '@eg/core/store.service';
-import {EgFormatService} from '@eg/share/util/format.service';
-
-
-@Injectable()
-export class EgGridService {
-
- constructor(
- private idl: EgIdlService,
- private org: EgOrgService,
- private store: EgStoreService,
- private pcrud: EgPcrudService,
- private format: EgFormatService
- ) {
- }
-
- getRowIndex(row: any, columnSet: EgGridColumnSet): any {
- let col = columnSet.indexColumn;
- if (!col) throw new Error('grid index column required');
- return this.getRowColumnValue(row, col);
- }
-
- getRowColumnValue(row: any, col: EgGridColumn): string {
- let val;
- if (typeof row[col.name] == 'function') {
- val = row[col.name]();
- } else {
- val = row[col.name]
- }
- return this.format.transform({value: val, datatype: col.datatype});
- }
-
- generateColumns(columnSet: EgGridColumnSet) {
-
- // generate columns for all non-virtual fields on the IDL class
- if (columnSet.idlClass) {
- this.idl.classes[columnSet.idlClass].fields.forEach(field => {
- if (field.virtual) return;
- let col = new EgGridColumn();
- col.name = field.name;
- col.label = field.label || field.name;
- col.idlFieldDef = field;
- col.datatype = field.datatype;
- if (field.name == this.idl.classes[columnSet.idlClass].pkey)
- col.isIndex = true;
- columnSet.add(col);
- });
- }
-
- return columnSet;
- }
-
-
- saveColumns(columnSet: EgGridColumnSet, persistKey: string): Promise<any> {
- if (!persistKey)
- throw new Error('Grid persistKey required to save columns');
- let compiled: EgGridColumnPersistConf[] = columnSet.compileSaveObject();
- return this.store.setItem('eg.grid.' + persistKey, compiled);
- }
-
-
- // TODO: saveColumnsAsOrgSetting(...)
-
- getColumnsConfig(persistKey: string): Promise<EgGridColumnPersistConf[]> {
- if (!persistKey) return Promise.resolve([]);
- return this.store.getItem('eg.grid.' + persistKey);
- }
-
-}
-
-
-export class EgGridColumn {
- name: string;
- path: string;
- label: string;
- flex: number;
- align: string;
- hidden: boolean;
- visible: boolean;
- sort: number;
- idlClass: string;
- idlFieldDef: any;
- datatype: string;
- cellTemplate: TemplateRef<any>;
- isIndex: boolean;
- isDragTarget: boolean;
- isSortable: boolean;
- isMultiSortable: boolean;
-}
-
-export class EgGridColumnPersistConf {
- name: string;
- flex?: number;
- sort?: number;
- align?: string;
-}
-
-export class EgGridColumnSet {
- columns: EgGridColumn[];
- idlClass: string;
- indexColumn: EgGridColumn;
- isSortable: boolean;
- isMultiSortable: boolean;
- stockVisible: string[];
-
- constructor(idlClass?: string) {
- this.columns = [];
- this.stockVisible = [];
- this.idlClass = idlClass;
- }
-
- add(col: EgGridColumn) {
- // avoid dupes
- if (this.getColByName(col.name)) return;
-
- if (col.isIndex) this.indexColumn = col;
- if (!col.flex) col.flex = 2;
- if (!col.label) col.label = col.name;
- if (!col.align) col.align = 'left';
- if (!col.datatype) col.datatype = 'text';
-
- col.visible = !col.hidden;
-
- // track which fields are visible on page load.
- if (col.visible) this.stockVisible.push(col.name);
-
- this.applyColumnSortability(col);
-
- this.columns.push(col);
- }
-
- getColByName(name: string): EgGridColumn {
- return this.columns.filter(c => c.name == name)[0];
- }
-
- reset() {
- this.columns.forEach(col => {
- col.flex = 2;
- col.sort = 0;
- col.align = 'left';
- col.visible = this.stockVisible.includes(col.name);
- });
- }
-
- applyColumnSortability(col: EgGridColumn) {
- // column sortability defaults to the sortability of the column set.
- if (col.isSortable === undefined && this.isSortable)
- col.isSortable = true;
-
- if (col.isMultiSortable === undefined && this.isMultiSortable)
- col.isMultiSortable = true;
-
- if (col.isMultiSortable) col.isSortable = true;
- }
-
- displayColumns(): EgGridColumn[] {
- return this.columns.filter(c => c.visible);
- }
-
- insertBefore(source: EgGridColumn, target: EgGridColumn) {
- let targetIdx = -1;
- let sourceIdx = -1;
- this.columns.forEach((col, idx) => {
- if (col.name == target.name) targetIdx = idx; });
-
- this.columns.forEach((col, idx) => {
- if (col.name == source.name) sourceIdx = idx; });
-
- if (sourceIdx >= 0)
- this.columns.splice(sourceIdx, 1);
-
- this.columns.splice(targetIdx, 0, source);
- }
-
- // Move visible columns to the front of the list.
- moveVisibleToFront() {
- let newCols = this.displayColumns();
- this.columns.forEach(col => { if (!col.visible) newCols.push(col) });
- this.columns = newCols;
- }
-
- moveColumn(col: EgGridColumn, diff: number) {
- var srcIdx, targetIdx;
-
- this.columns.forEach((c, i) => {
- if (c.name == col.name) srcIdx = i
- });
-
- targetIdx = srcIdx + diff;
- if (targetIdx < 0) {
- targetIdx = 0;
- } else if (targetIdx >= this.columns.length) {
- // Target index follows the last visible column.
- var lastVisible = 0;
- this.columns.forEach((c, idx) => {
- if (c.visible) lastVisible = idx;
- });
-
- // When moving a column (down) causes one or more
- // visible columns to shuffle forward, our column
- // moves into the slot of the last visible column.
- // Otherwise, put it into the slot directly following
- // the last visible column.
- targetIdx = srcIdx <= lastVisible ? lastVisible : lastVisible + 1;
- }
-
- // Splice column out of old position, insert at new position.
- this.columns.splice(srcIdx, 1);
- this.columns.splice(targetIdx, 0, col);
- }
-
- compileSaveObject(): EgGridColumnPersistConf[] {
- // only store information about visible columns.
- let conf = this.displayColumns();
-
- // scrunch the data down to just the needed info
- return conf.map(col => {
- let c: EgGridColumnPersistConf = {name : col.name};
- if (col.align != 'left') c.align = col.align;
- if (col.flex != 2) c.flex = col.flex;
- if (Number(col.sort)) c.sort = Number(c.sort);
- return c;
- });
- }
-
- applyColumnSettings(conf: EgGridColumnPersistConf[]) {
- if (!conf || conf.length == 0) return;
-
- let newCols = [];
-
- conf.forEach(colConf => {
- let col = this.getColByName(colConf.name);
- if (!col) return; // no such column in this grid.
-
- col.visible = true;
- if (colConf.align) col.align = colConf.align;
- if (colConf.flex) col.flex = colConf.flex;
- if (colConf.sort) col.sort = colConf.sort;
-
- // Add to new columns array, avoid dupes.
- if (newCols.filter(c => c.name == col.name).length == 0)
- newCols.push(col);
- });
-
- // columns which are not expressed within the saved
- // configuration are marked as non-visible and
- // appended to the end of the new list of columns.
- this.columns.forEach(c => {
- if (conf.filter(cf => cf.name == c.name).length == 0) {
- c.visible = false;
- newCols.push(c);
- }
- });
-
- this.columns = newCols;
- }
-}
-
-// Actions apply to specific rows
-export class EgGridToolbarAction {
- label: string;
- action: (rows: any[]) => any;
-}
-
-// Buttons are global actions
-export class EgGridToolbarButton {
- label: string;
- action: () => any;
-}
-
-export class EgGridRowSelector {
- indexes: {[string:string] : boolean};
-
- constructor() {
- this.clear();
- }
-
- // Returns true if all of the requested indexes exist in the selector.
- contains(index: string | string[]): boolean {
- let indexes = [].concat(index);
- for (let i = 0; i < indexes.length; i++) { // early exit
- if (!this.indexes[indexes[i]])
- return false;
- }
- return true;
- }
-
- select(index: string | string[]) {
- let indexes = [].concat(index);
- indexes.forEach(i => this.indexes[i] = true);
- }
-
- deselect(index: string | string[]) {
- let indexes = [].concat(index);
- indexes.forEach(i => delete this.indexes[i]);
- }
-
- // Returns the list of selected index values.
- // in some contexts (template checkboxes) the value for an index is
- // set to false to deselect instead of having it removed (via deselect()).
- selected() {
- return Object.keys(this.indexes).filter(
- ind => {return Boolean(this.indexes[ind])})
- }
-
- isEmpty(): boolean {
- return this.selected().length == 0;
- }
-
- clear() {
- this.indexes = {};
- }
-}
-
--- /dev/null
+/**
+ * Collection of grid related classses and interfaces.
+ */
+import {TemplateRef} from '@angular/core';
+import {Observable} from 'rxjs/Rx';
+import {Subscription} from "rxjs/Subscription";
+import {EgIdlService, EgIdlObject} from '@eg/core/idl.service';
+import {EgOrgService} from '@eg/core/org.service';
+import {EgStoreService} from '@eg/core/store.service';
+import {EgFormatService} from '@eg/share/util/format.service';
+import {Pager} from '@eg/share/util/pager';
+
+export class EgGridContext {
+
+ pager: Pager;
+ idlClass: string;
+ isSortable: boolean;
+ isMultiSortable: boolean;
+ persistKey: string;
+ disableMultiSelect: boolean;
+ dataSource: EgGridDataSource;
+ columnSet: EgGridColumnSet;
+ rowSelector: EgGridRowSelector;
+ toolbarButtons: EgGridToolbarButton[];
+ toolbarActions: EgGridToolbarAction[];
+ lastSelectedIndex: any;
+ pageChanges: Subscription;
+
+ // Services injected by our grid component
+ idl: EgIdlService;
+ org: EgOrgService;
+ store: EgStoreService;
+ format: EgFormatService;
+
+ constructor(
+ idl: EgIdlService, org: EgOrgService,
+ store: EgStoreService, format: EgFormatService) {
+
+ this.idl = idl;
+ this.org = org;
+ this.store = store;
+ this.format = format;
+ this.pager = new Pager();
+ this.pager.limit = 10; // TODO config
+ this.rowSelector = new EgGridRowSelector();
+ this.toolbarButtons = [];
+ this.toolbarActions = [];
+ }
+
+ init() {
+ this.columnSet = new EgGridColumnSet(this.idlClass);
+ this.columnSet.isSortable = this.isSortable === true;
+ this.columnSet.isMultiSortable = this.isMultiSortable === true;
+ this.generateColumns();
+ }
+
+ // Load initial settings and data.
+ initData() {
+ this.getColumnsConfig(this.persistKey)
+ .then(conf => this.columnSet.applyColumnSettings(conf))
+ .then(ok => this.dataSource.requestPage(this.pager))
+ .then(ok => this.listenToPager())
+ }
+
+ destroy() {
+ this.ignorePager();
+
+ }
+ reload() {
+ this.pager.reset();
+ this.dataSource.reset();
+ this.dataSource.requestPage(this.pager);
+ }
+
+ // Subscribe or unsubscribe to page-change events from the pager.
+ listenToPager() {
+ if (this.pageChanges) return;
+ this.pageChanges = this.pager.onChange$.subscribe(
+ val => this.dataSource.requestPage(this.pager));
+ // TODO: index rows as they arrive
+ }
+
+ ignorePager() {
+ if (!this.pageChanges) return;
+ this.pageChanges.unsubscribe();
+ this.pageChanges = null
+ }
+
+ getRowIndex(row: any): any {
+ let col = this.columnSet.indexColumn;
+ if (!col) throw new Error('grid index column required');
+ return this.getRowColumnValue(row, col);
+ }
+
+ // Returns the array position of the row-by-index in the
+ // dataSource array.
+ getRowPosition(index: any): number {
+ // for-loop for early exit
+ for (let idx = 0; idx < this.dataSource.data.length; idx++) {
+ if (index == this.getRowIndex(this.dataSource.data[idx]))
+ return idx;
+ }
+ }
+
+ getRowByIndex(index: any): any {
+ for (let idx = 0; idx < this.dataSource.data.length; idx++) {
+ if (index == this.getRowIndex(this.dataSource.data[idx]))
+ return this.dataSource.data[idx];
+ }
+ }
+
+ // Returns all selected rows, regardless of whether they are
+ // currently visible in the grid display.
+ getSelectedRows(): any[] {
+ let selected = [];
+ this.rowSelector.selected().forEach(index => {
+ let row = this.getRowByIndex(index);
+ if (row) selected.push(row);
+ });
+ return selected;
+ }
+
+ getRowColumnValue(row: any, col: EgGridColumn): string {
+ let val;
+ if (typeof row[col.name] == 'function') {
+ val = row[col.name]();
+ } else {
+ val = row[col.name]
+ }
+ return this.format.transform({value: val, datatype: col.datatype});
+ }
+
+ selectOneRow(index: any) {
+ this.rowSelector.clear();
+ this.rowSelector.select(index);
+ this.lastSelectedIndex = index;
+ }
+
+ // selects or deselects an item, without affecting the others.
+ // returns true if the item is selected; false if de-selected.
+ toggleSelectOneRow(index: any) {
+ if (this.rowSelector.contains(index)) {
+ this.rowSelector.deselect(index);
+ return false;
+ }
+
+ this.rowSelector.select(index);
+ return true;
+ }
+
+ selectRowByPos(pos: number) {
+ let row = this.dataSource.data[pos];
+ if (row) this.selectOneRow(this.getRowIndex(row));
+ }
+
+ selectPreviousRow() {
+ if (!this.lastSelectedIndex) return;
+ let pos = this.getRowPosition(this.lastSelectedIndex);
+ if (pos == this.pager.offset) {
+ this.toPrevPage().then(ok => this.selectLastRow(), err => {});
+ } else {
+ this.selectRowByPos(pos - 1);
+ }
+ }
+
+ selectNextRow() {
+ if (!this.lastSelectedIndex) return;
+ let pos = this.getRowPosition(this.lastSelectedIndex);
+ if (pos == (this.pager.offset + this.pager.limit - 1)) {
+ this.toNextPage().then(ok => this.selectFirstRow(), err => {});
+ } else {
+ this.selectRowByPos(pos + 1);
+ }
+ }
+
+ selectFirstRow() {
+ this.selectRowByPos(this.pager.offset);
+ }
+
+ selectLastRow() {
+ this.selectRowByPos(this.pager.offset + this.pager.limit - 1);
+ }
+
+ toPrevPage(): Promise<any> {
+ if (this.pager.isFirstPage()) return Promise.reject('on first');
+ // temp ignore pager events since we're calling requestPage manually.
+ this.ignorePager();
+ this.pager.decrement();
+ this.listenToPager();
+ return this.dataSource.requestPage(this.pager);
+ }
+
+ toNextPage(): Promise<any> {
+ if (this.pager.isLastPage()) return Promise.reject('on last');
+ // temp ignore pager events since we're calling requestPage manually.
+ this.ignorePager();
+ this.pager.increment();
+ this.listenToPager();
+ return this.dataSource.requestPage(this.pager);
+ }
+
+
+ generateColumns() {
+
+ // generate columns for all non-virtual fields on the IDL class
+ if (this.columnSet.idlClass) {
+ this.idl.classes[this.columnSet.idlClass].fields.forEach(field => {
+ if (field.virtual) return;
+ let col = new EgGridColumn();
+ col.name = field.name;
+ col.label = field.label || field.name;
+ col.idlFieldDef = field;
+ col.datatype = field.datatype;
+ if (field.name == this.idl.classes[this.columnSet.idlClass].pkey)
+ col.isIndex = true;
+ this.columnSet.add(col);
+ });
+ }
+ }
+
+
+ saveColumns(): Promise<any> {
+ if (!this.persistKey)
+ throw new Error('Grid persistKey required to save columns');
+ let compiled: EgGridColumnPersistConf[] = this.columnSet.compileSaveObject();
+ return this.store.setItem('eg.grid.' + this.persistKey, compiled);
+ }
+
+
+ // TODO: saveColumnsAsOrgSetting(...)
+
+ getColumnsConfig(persistKey: string): Promise<EgGridColumnPersistConf[]> {
+ if (!persistKey) return Promise.resolve([]);
+ return this.store.getItem('eg.grid.' + persistKey);
+ }
+
+}
+
+export class EgGridColumn {
+ name: string;
+ path: string;
+ label: string;
+ flex: number;
+ align: string;
+ hidden: boolean;
+ visible: boolean;
+ sort: number;
+ idlClass: string;
+ idlFieldDef: any;
+ datatype: string;
+ cellTemplate: TemplateRef<any>;
+ isIndex: boolean;
+ isDragTarget: boolean;
+ isSortable: boolean;
+ isMultiSortable: boolean;
+}
+
+export class EgGridColumnPersistConf {
+ name: string;
+ flex?: number;
+ sort?: number;
+ align?: string;
+}
+
+export class EgGridColumnSet {
+ columns: EgGridColumn[];
+ idlClass: string;
+ indexColumn: EgGridColumn;
+ isSortable: boolean;
+ isMultiSortable: boolean;
+ stockVisible: string[];
+
+ constructor(idlClass?: string) {
+ this.columns = [];
+ this.stockVisible = [];
+ this.idlClass = idlClass;
+ }
+
+ add(col: EgGridColumn) {
+ // avoid dupes
+ if (this.getColByName(col.name)) return;
+
+ if (col.isIndex) this.indexColumn = col;
+ if (!col.flex) col.flex = 2;
+ if (!col.label) col.label = col.name;
+ if (!col.align) col.align = 'left';
+ if (!col.datatype) col.datatype = 'text';
+
+ col.visible = !col.hidden;
+
+ // track which fields are visible on page load.
+ if (col.visible) this.stockVisible.push(col.name);
+
+ this.applyColumnSortability(col);
+
+ this.columns.push(col);
+ }
+
+ getColByName(name: string): EgGridColumn {
+ return this.columns.filter(c => c.name == name)[0];
+ }
+
+ reset() {
+ this.columns.forEach(col => {
+ col.flex = 2;
+ col.sort = 0;
+ col.align = 'left';
+ col.visible = this.stockVisible.includes(col.name);
+ });
+ }
+
+ applyColumnSortability(col: EgGridColumn) {
+ // column sortability defaults to the sortability of the column set.
+ if (col.isSortable === undefined && this.isSortable)
+ col.isSortable = true;
+
+ if (col.isMultiSortable === undefined && this.isMultiSortable)
+ col.isMultiSortable = true;
+
+ if (col.isMultiSortable) col.isSortable = true;
+ }
+
+ displayColumns(): EgGridColumn[] {
+ return this.columns.filter(c => c.visible);
+ }
+
+ insertBefore(source: EgGridColumn, target: EgGridColumn) {
+ let targetIdx = -1;
+ let sourceIdx = -1;
+ this.columns.forEach((col, idx) => {
+ if (col.name == target.name) targetIdx = idx; });
+
+ this.columns.forEach((col, idx) => {
+ if (col.name == source.name) sourceIdx = idx; });
+
+ if (sourceIdx >= 0)
+ this.columns.splice(sourceIdx, 1);
+
+ this.columns.splice(targetIdx, 0, source);
+ }
+
+ // Move visible columns to the front of the list.
+ moveVisibleToFront() {
+ let newCols = this.displayColumns();
+ this.columns.forEach(col => { if (!col.visible) newCols.push(col) });
+ this.columns = newCols;
+ }
+
+ moveColumn(col: EgGridColumn, diff: number) {
+ var srcIdx, targetIdx;
+
+ this.columns.forEach((c, i) => {
+ if (c.name == col.name) srcIdx = i
+ });
+
+ targetIdx = srcIdx + diff;
+ if (targetIdx < 0) {
+ targetIdx = 0;
+ } else if (targetIdx >= this.columns.length) {
+ // Target index follows the last visible column.
+ var lastVisible = 0;
+ this.columns.forEach((c, idx) => {
+ if (c.visible) lastVisible = idx;
+ });
+
+ // When moving a column (down) causes one or more
+ // visible columns to shuffle forward, our column
+ // moves into the slot of the last visible column.
+ // Otherwise, put it into the slot directly following
+ // the last visible column.
+ targetIdx = srcIdx <= lastVisible ? lastVisible : lastVisible + 1;
+ }
+
+ // Splice column out of old position, insert at new position.
+ this.columns.splice(srcIdx, 1);
+ this.columns.splice(targetIdx, 0, col);
+ }
+
+ compileSaveObject(): EgGridColumnPersistConf[] {
+ // only store information about visible columns.
+ let conf = this.displayColumns();
+
+ // scrunch the data down to just the needed info
+ return conf.map(col => {
+ let c: EgGridColumnPersistConf = {name : col.name};
+ if (col.align != 'left') c.align = col.align;
+ if (col.flex != 2) c.flex = col.flex;
+ if (Number(col.sort)) c.sort = Number(c.sort);
+ return c;
+ });
+ }
+
+ applyColumnSettings(conf: EgGridColumnPersistConf[]) {
+ if (!conf || conf.length == 0) return;
+
+ let newCols = [];
+
+ conf.forEach(colConf => {
+ let col = this.getColByName(colConf.name);
+ if (!col) return; // no such column in this grid.
+
+ col.visible = true;
+ if (colConf.align) col.align = colConf.align;
+ if (colConf.flex) col.flex = colConf.flex;
+ if (colConf.sort) col.sort = colConf.sort;
+
+ // Add to new columns array, avoid dupes.
+ if (newCols.filter(c => c.name == col.name).length == 0)
+ newCols.push(col);
+ });
+
+ // columns which are not expressed within the saved
+ // configuration are marked as non-visible and
+ // appended to the end of the new list of columns.
+ this.columns.forEach(c => {
+ if (conf.filter(cf => cf.name == c.name).length == 0) {
+ c.visible = false;
+ newCols.push(c);
+ }
+ });
+
+ this.columns = newCols;
+ }
+}
+
+// Actions apply to specific rows
+export class EgGridToolbarAction {
+ label: string;
+ action: (rows: any[]) => any;
+}
+
+// Buttons are global actions
+export class EgGridToolbarButton {
+ label: string;
+ action: () => any;
+}
+
+export class EgGridRowSelector {
+ indexes: {[string:string] : boolean};
+
+ constructor() {
+ this.clear();
+ }
+
+ // Returns true if all of the requested indexes exist in the selector.
+ contains(index: string | string[]): boolean {
+ let indexes = [].concat(index);
+ for (let i = 0; i < indexes.length; i++) { // early exit
+ if (!this.indexes[indexes[i]])
+ return false;
+ }
+ return true;
+ }
+
+ select(index: string | string[]) {
+ let indexes = [].concat(index);
+ indexes.forEach(i => this.indexes[i] = true);
+ }
+
+ deselect(index: string | string[]) {
+ let indexes = [].concat(index);
+ indexes.forEach(i => delete this.indexes[i]);
+ }
+
+ // Returns the list of selected index values.
+ // in some contexts (template checkboxes) the value for an index is
+ // set to false to deselect instead of having it removed (via deselect()).
+ selected() {
+ return Object.keys(this.indexes).filter(
+ ind => {return Boolean(this.indexes[ind])})
+ }
+
+ isEmpty(): boolean {
+ return this.selected().length == 0;
+ }
+
+ clear() {
+ this.indexes = {};
+ }
+}
+
+export class EgGridDataSource {
+
+ data: any[];
+ sort: any[];
+ allRowsRetrieved: boolean;
+ getRows: (pager: Pager, sort: any[]) => Observable<any>;
+ indexedRows: {[index:string] : any};
+
+ constructor() {
+ this.sort = [];
+ this.reset();
+ }
+
+ reset() {
+ this.data = [];
+ this.allRowsRetrieved = false;
+ }
+
+ // called from the template -- no data fetching
+ getPageOfRows(pager: Pager): any[] {
+ if (this.data) {
+ return this.data.slice(
+ pager.offset, pager.limit + pager.offset);
+ }
+ return [];
+ }
+
+ // called on initial component load and user action (e.g. paging, sorting).
+ requestPage(pager: Pager): Promise<any> {
+
+ if (
+ // already have the current page
+ this.getPageOfRows(pager).length > 0
+ // already have all data
+ || this.allRowsRetrieved
+ // have no way to get more data.
+ || !this.getRows
+ ) {
+ return Promise.resolve();
+ }
+
+ return new Promise((resolve, reject) => {
+ let idx = pager.offset;
+ return this.getRows(pager, this.sort).subscribe(
+ row => this.data[idx++] = row,
+ err => {
+ console.error(`grid getRows() error ${err}`);
+ reject(err);
+ },
+ () => {
+ this.checkAllRetrieved(pager, idx);
+ resolve();
+ }
+ );
+ });
+ }
+
+ // See if the last getRows() call resulted in the final set of data.
+ checkAllRetrieved(pager: Pager, idx: number) {
+ if (this.allRowsRetrieved) return;
+
+ if (idx == 0 || idx < (pager.limit + pager.offset)) {
+ // last query returned nothing or less than one page.
+ // confirm we have all of the preceding pages.
+ if (!this.data.includes(undefined)) {
+ this.allRowsRetrieved = true;
+ pager.resultCount = this.data.length;
+ }
+ }
+ }
+}
+
+
import {EgIdlService, EgIdlObject} from '@eg/core/idl.service';
import {EgOrgService} from '@eg/core/org.service';
+declare var OpenSRF;
+
export interface EgFormatParams {
value: any;
idlClass?: string;
idlField?: string;
datatype?: string;
orgField?: string; // 'shortname' || 'name'
+ datePlusTime?: boolean;
}
@Injectable()
export class EgFormatService {
+ dateFormat: string = 'shortDate';
+ dateTimeFormat: string = 'short';
+ wsOrgTimezone: string = OpenSRF.tz;
+
constructor(
private datePipe: DatePipe,
private currencyPipe: CurrencyPipe,
case 'timestamp':
let date = new Date(value);
- // TODO: date format settings and options.
- return this.datePipe.transform(date, 'yyyy-MM-dd');
+ let fmt = this.dateFormat || 'shortDate';
+ if (params.datePlusTime) {
+ fmt = this.dateTimeFormat || 'short';
+ }
+ return this.datePipe.transform(date, fmt);
case 'money':
return this.currencyPipe.transform(value);
this.onChange$ = new EventEmitter<number>();
}
+ reset() {
+ this.resultCount = null;
+ this.offset = 0;
+ }
+
isFirstPage(): boolean {
return this.offset == 0;
}
import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {EgIdlObject} from '@eg/core/idl.service';
-import {EgGridDataSource} from '@eg/share/grid/grid-data-source';
+import {EgGridDataSource} from '@eg/share/grid/grid';
import {EgGridComponent} from '@eg/share/grid/grid.component';
import {EgToastService} from '@eg/share/toast/toast.service';
import {Pager} from '@eg/share/util/pager';
import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {EgIdlObject} from '@eg/core/idl.service';
-import {EgGridDataSource} from '@eg/share/grid/grid-data-source';
+import {EgGridDataSource} from '@eg/share/grid/grid';
import {EgGridComponent} from '@eg/share/grid/grid.component';
import {EgToastService} from '@eg/share/toast/toast.service';
import {Pager} from '@eg/share/util/pager';
import {EgNetService} from '@eg/core/net.service';
import {EgAuthService, EgAuthWsState} from '@eg/core/auth.service';
import {EgPermService} from '@eg/core/perm.service';
+import {EgOrgService} from '@eg/core/org.service';
+import {EgFormatService} from '@eg/share/util/format.service';
const LOGIN_PATH = '/staff/login';
const WS_MANAGE_PATH = '/staff/admin/workstation/workstations/manage';
private route: ActivatedRoute,
private ngLocation: Location,
private store: EgStoreService,
+ private org: EgOrgService,
private net: EgNetService,
private auth: EgAuthService,
private perm: EgPermService,
+ private format: EgFormatService
) {}
resolve(
*/
loadStartupData(): Promise<void> {
console.debug('EgStaffResolver:loadStartupData()');
- return Promise.resolve();
+
+ // Fetch settings needed globally. This will cache the values
+ // in the org service.
+ return this.org.settings([
+ 'lib.timezone',
+ 'webstaff.format.dates',
+ 'webstaff.format.date_and_time',
+ 'ui.staff.max_recent_patrons'
+ ]).then(settings => {
+ this.format.wsOrgTimezone = settings['lib.timezone'];
+ this.format.dateFormat = settings['webstaff.format.dates'];
+ this.format.dateTimeFormat = settings['webstaff.format.date_and_time'];
+ });
}
}
import {EgToastService} from '@eg/share/toast/toast.service';
import {EgStringService} from '@eg/share/string/string.service';
import {Observable} from 'rxjs/Rx';
-import {EgGridDataSource} from '@eg/share/grid/grid-data-source';
+import {EgGridDataSource} from '@eg/share/grid/grid';
import {EgIdlService, EgIdlObject} from '@eg/core/idl.service';
import {EgPcrudService} from '@eg/core/pcrud.service';
import {Pager} from '@eg/share/util/pager';