*/
import {TemplateRef} from '@angular/core';
import {Observable} from 'rxjs/Observable';
-import {Subscription} from "rxjs/Subscription";
+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';
const MAX_ALL_ROW_COUNT = 10000;
+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 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() {
+ const newCols = this.displayColumns();
+ this.columns.forEach(col => {
+ if (!col.visible) { newCols.push(col); }});
+ this.columns = newCols;
+ }
+
+ moveColumn(col: EgGridColumn, diff: number) {
+ let 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.
+ let 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.
+ const conf = this.displayColumns();
+
+ // scrunch the data down to just the needed info
+ return conf.map(col => {
+ const c: EgGridColumnPersistConf = {name : col.name};
+ if (col.align !== 'left') { c.align = col.align; }
+ if (col.flex !== 2) { c.flex = Number(col.flex); }
+ if (Number(col.sort)) { c.sort = Number(c.sort); }
+ return c;
+ });
+ }
+
+ applyColumnSettings(conf: EgGridColumnPersistConf[]) {
+ if (!conf || conf.length === 0) { return; }
+
+ const newCols = [];
+
+ conf.forEach(colConf => {
+ const 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 = Number(colConf.flex); }
+ if (colConf.sort) { col.sort = Number(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;
+ }
+}
+
+
+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 {
+ const 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[]) {
+ const indexes = [].concat(index);
+ indexes.forEach(i => this.indexes[i] = true);
+ }
+
+ deselect(index: string | string[]) {
+ const 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 => Boolean(this.indexes[ind]));
+ }
+
+ isEmpty(): boolean {
+ return this.selected().length === 0;
+ }
+
+ clear() {
+ this.indexes = {};
+ }
+}
+
+
+
export class EgGridContext {
pager: Pager;
this.getColumnsConfig(this.persistKey)
.then(conf => this.columnSet.applyColumnSettings(conf))
.then(ok => this.dataSource.requestPage(this.pager))
- .then(ok => this.listenToPager())
+ .then(ok => this.listenToPager());
}
destroy() {
// Subscribe or unsubscribe to page-change events from the pager.
listenToPager() {
- if (this.pageChanges) return;
+ 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;
+ if (!this.pageChanges) { return; }
this.pageChanges.unsubscribe();
- this.pageChanges = null
+ this.pageChanges = null;
}
getRowIndex(row: any): any {
- let col = this.columnSet.indexColumn;
- if (!col) throw new Error('grid index column required');
+ const col = this.columnSet.indexColumn;
+ if (!col) {
+ throw new Error('grid index column required');
+ }
return this.getRowColumnValue(row, col);
}
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]))
+ 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]))
+ 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 = [];
+ const selected = [];
this.rowSelector.selected().forEach(index => {
- let row = this.getRowByIndex(index);
- if (row) selected.push(row);
+ const 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') {
+ if (typeof row[col.name] === 'function') {
val = row[col.name]();
} else {
- val = row[col.name]
+ val = row[col.name];
}
return this.format.transform({value: val, datatype: col.datatype});
}
}
selectRowByPos(pos: number) {
- let row = this.dataSource.data[pos];
- if (row) this.selectOneRow(this.getRowIndex(row));
+ const 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) {
+ if (!this.lastSelectedIndex) { return; }
+ const 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)) {
+ if (!this.lastSelectedIndex) { return; }
+ const 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);
}
toPrevPage(): Promise<any> {
- if (this.pager.isFirstPage()) return Promise.reject('on first');
+ if (this.pager.isFirstPage()) {
+ return Promise.reject('on first');
+ }
// temp ignore pager events since we're calling requestPage manually.
this.ignorePager();
this.pager.decrement();
}
toNextPage(): Promise<any> {
- if (this.pager.isLastPage()) return Promise.reject('on last');
+ if (this.pager.isLastPage()) {
+ return Promise.reject('on last');
+ }
// temp ignore pager events since we're calling requestPage manually.
this.ignorePager();
this.pager.increment();
}
getAllRows(): Promise<any> {
- let pager = new Pager();
+ const pager = new Pager();
pager.offset = 0;
pager.limit = MAX_ALL_ROW_COUNT;
return this.dataSource.requestPage(pager);
// Returns a key/value pair object of visible column data as text.
getRowAsFlatText(row: any): any {
- let flatRow = {};
+ const flatRow = {};
this.columnSet.displayColumns().forEach(col => {
flatRow[col.name] =
this.getColumnTextContent(row, col);
this.getAllRows().then(ok => {
this.dataSource.data.forEach(row => {
observer.next(this.getRowAsFlatText(row));
- })
+ });
observer.complete();
});
});
gridToCsv(): Promise<string> {
let csvStr = '';
- let columns = this.columnSet.displayColumns();
+ const columns = this.columnSet.displayColumns();
// CSV header
columns.forEach(col => {
csvStr += ',';
});
- csvStr = csvStr.replace(/,$/,'\n');
+ csvStr = csvStr.replace(/,$/, '\n');
return new Promise(resolve => {
this.getAllRowsAsText().subscribe(
csvStr += this.valueToCsv(row[col.name]);
csvStr += ',';
});
- csvStr = csvStr.replace(/,$/,'\n');
+ csvStr = csvStr.replace(/,$/, '\n');
},
err => {},
() => resolve(csvStr)
// prepares a string for inclusion within a CSV document
// by escaping commas and quotes and removing newlines.
valueToCsv(str: string): string {
- str = ''+str;
- if (!str) return '';
+ str = '' + str;
+ if (!str) { return ''; }
str = str.replace(/\n/g, '');
if (str.match(/\,/) || str.match(/"/)) {
str = str.replace(/"/g, '""');
// 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();
+ if (field.virtual) { return; }
+ const 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)
+ if (field.name ===
+ this.idl.classes[this.columnSet.idlClass].pkey) {
col.isIndex = true;
+ }
this.columnSet.add(col);
});
}
saveColumns(): Promise<any> {
- if (!this.persistKey)
+ if (!this.persistKey) {
throw new Error('Grid persistKey required to save columns');
- let compiled: EgGridColumnPersistConf[] = this.columnSet.compileSaveObject();
+ }
+ const 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([]);
+ 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;
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 {
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 = [];
requestPage(pager: Pager): Promise<any> {
if (
- this.getPageOfRows(pager).length == pager.limit
+ this.getPageOfRows(pager).length === pager.limit
// already have all data
|| this.allRowsRetrieved
// have no way to get more data.
// See if the last getRows() call resulted in the final set of data.
checkAllRetrieved(pager: Pager, idx: number) {
- if (this.allRowsRetrieved) return;
+ if (this.allRowsRetrieved) { return; }
- if (idx == 0 || idx < (pager.limit + pager.offset)) {
+ 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)) {