blob: 674cb6be3da5dfafd796ea286c7dbd6bb076535c [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
import { Component, Input, ViewChild, OnInit, OnChanges, SimpleChanges, DoCheck} from '@angular/core';
import { View, ViewColumn} from './tableview.service';
import { NavigatorService } from '../navigator/navigator.service';
import { SearchAttribute } from '../search/search.service';
import { BasketService} from '../basket/basket.service';
import { EditViewComponent } from './editview.component';
import { SearchResult, Row } from './query.service';
import { Node } from '../navigator/node';
import { NodeService } from '../navigator/node.service';
import {DataTableModule, SharedModule, ContextMenuModule, MenuItem, SortEvent} from 'primeng/primeng';
import {MDMNotificationService} from '../core/mdm-notification.service';
import { TranslateService } from '@ngx-translate/core';
import { streamTranslate, TRANSLATE } from '../core/mdm-core.module';
export class TurboViewColumn {
field: string;
header: string;
width: number;
sortOrder: number;
constructor (field: string, header: string, width?: number, sortOrder?: number) {
this.field = field;
this.header = header;
this.sortOrder = sortOrder != undefined ? sortOrder : 0;
this.width = width;
}
}
@Component({
selector: 'mdm-tableview',
templateUrl: 'tableview.component.html',
styleUrls: ['./tableview.component.css']
})
export class TableviewComponent implements OnInit, OnChanges {
public static readonly pageSize = 5;
readonly buttonColumns = 3;
@Input() view: View;
@Input() results: SearchResult;
/** @todo: Bad practice. value defined in template will be assumed to be a string.
example: in template: [isShopable]="false", in component if(isShopable) {console.log('yes')} else { console.log('no')} will log yes!
**/
@Input() isShopable = false;
@Input() isRemovable = false;
@Input() menuItems: MenuItem[] = [];
@Input() selectedEnvs: Node[];
@Input() searchAttributes: { [env: string]: SearchAttribute[] };
@Input() environments: Node[];
@Input() loading: true;
@Input() loadingIcon = 'fa-spinner';
public menuSelectedRow: Row;
public selectedRows: Row[] = [];
public columnsToShow: ViewColumn[];
public readonly buttonColStyle = {'width': '3%'};
public btnColHidden = false;
public viewRows: Row[]
public turboViewColumns: TurboViewColumn[];
public selectedViewRows: Row[] = [];
constructor(private basketService: BasketService,
private navigatorService: NavigatorService,
private nodeService: NodeService,
private notificationService: MDMNotificationService,
private translateService: TranslateService) {
}
ngOnInit() {
streamTranslate(this.translateService, TRANSLATE('tableview.tableview.show-in-tree'))
.subscribe(labels => this.menuItems.push({
label: labels,
icon: 'fa fa-tree',
command: (event) => this.openInTree(this.menuSelectedRow)
}));
streamTranslate(this.translateService, TRANSLATE('tableview.tableview.reset-selection'))
.subscribe(labels => this.menuItems.push({
label: labels,
icon: 'fa fa-square-o',
command: (event) => {
this.selectedViewRows = [];
this.menuSelectedRow = undefined;
}
}));
}
ngOnChanges(changes: SimpleChanges) {
if (changes['results'] && this.view != undefined) {
this.customSort({ 'field': this.view.getSortField(), 'order': this.view.getSortOrder() });
this.viewRows = this.results.rows.map(row => this.mapRow2View(row));
}
if (changes['view'] && this.view != undefined) {
this.turboViewColumns = this.view.columns.map(viewCol => this.mapColumn2Turbo(viewCol));
}
if (changes['selectedEnvs'] || changes['view']) {
this.updateColumnsToShow();
}
}
mapRow2View(row: Row) {
let viewRow: Row = new Row();
row.columns.forEach(col => viewRow[col.type + '_' + col.attribute] = col.value != undefined ? col.value : '-');
viewRow['id'] = row.id;
viewRow['type'] = row.type;
viewRow['source'] = row.source;
return viewRow;
}
mapColumn2Turbo(viewCol: ViewColumn) {
return new TurboViewColumn(viewCol.type + '_' + viewCol.name, viewCol.type + ' ' + viewCol.name, viewCol.width);
}
/** @TODO: update for viewRows/columns
**/
updateColumnsToShow() {
if (this.view && this.selectedEnvs && this.selectedEnvs.length > 0
&& this.searchAttributes && this.searchAttributes[this.selectedEnvs[0].sourceName]) {
let relevantCols: string[] = [];
this.selectedEnvs.forEach(env =>
relevantCols = relevantCols.concat(this.searchAttributes[env.sourceName].map(sa =>
sa.boType.toLowerCase() + sa.attrName.toLowerCase())
)
);
relevantCols = Array.from(new Set(relevantCols));
this.view.columns.filter(col => {
if (relevantCols.findIndex(ct => ct === col.type.toLowerCase() + col.name.toLowerCase()) > -1) {
col.hidden = false;
} else {
col.hidden = true;
}
this.btnColHidden = false;
});
} else if (this.view && this.selectedEnvs && this.selectedEnvs.length === 0) {
this.btnColHidden = true;
this.view.columns.forEach(vc => vc.hidden = true);
}
}
onContextMenuSelect(event: any) {
this.menuSelectedRow = event.data;
}
/** @todo: p-table is not supporting manual column width. Might be kept as guide line in case
* future version of p-table support checkbox with multi selection.
* @deprecated
**/
onColResize(event: any) {
let index = event.element.cellIndex - this.buttonColumns;
if (index > -1) {
this.view.columns[index].width = event.element.clientWidth;
}
}
onColReorder(event: any) {
let tmp = this.view.columns[event.dragIndex];
this.view.columns[event.dragIndex] = this.view.columns[ event.dropIndex];
this.view.columns[event.dropIndex] = tmp;
}
onSort(event: any) {
if (this.view != undefined) {
this.view.sortField = event.field;
this.view.sortOrder = event.order;
}
}
customSort(event: SortEvent) {
let comparer = function(row1: Row, row2: Row): number {
let value1 = row1[event.field];
let value2 = row2[event.field];
if (value1 == null && value2 != null) {
return -1;
} else if (value1 != null && value2 == null) {
return 1;
} else if (value1 == null && value2 == null) {
return 0;
} else if (!isNaN(<any>value1) && !isNaN(<any>value2)) {
if (value1 === value2) {
return 0;
} else {
return (+value1 < +value2 ? -1 : 1) * event.order;
}
} else {
return value1.localeCompare(value2) * -event.order;
}
};
if (event.data) {
event.data.sort(comparer);
}
}
functionalityProvider(row: Row) {
let item = row.getItem();
if (this.isShopable) {
this.basketService.add(item);
} else {
this.basketService.remove(item);
}
}
/** @todo: should be converted to pipe. avoid function calls from template (apart from event binding) for performance.
**/
getNodeClass(type: string) {
switch (type) {
case 'StructureLevel':
return 'pool';
case 'MeaResult':
return 'measurement';
case 'SubMatrix':
return 'channelgroup';
case 'MeaQuantity':
return 'channel';
default:
return type.toLowerCase();
}
}
/** @todo: should be converted to pipe. avoid function calls from template (apart from event binding) for performance.
**/
getRowTitle(row: Row) {
return this.translateService.instant('tableview.tableview.tooltip-open-in')
+ ': ' + [NodeService.mapSourceNameToName(this.environments, row.source), row.type, row.id].join('/');
}
/** @todo: should be converted to pipe. avoid function calls from template for performance.
* option b) convert to componant field, since its not depending on result data.
**/
getIconTitle() {
return this.isShopable
? TRANSLATE('tableview.tableview.tooltip-add-to-shopping-basket')
: TRANSLATE('tableview.tableview.tooltip-remove-from-shopping-basket');
}
/** @todo: helping function for multi select in p-datatable. p-table does not support select mode
* multiple with check box. Thus, this is not used at the moment. Might be kept as guide line in case
* future version of p-table support checkbox with multi selection.
* @deprecated
**/
onRowClick(e: any) {
let row: Row = e.data;
this.nodeService.getNodeFromItem(row.getItem()).subscribe(
node => this.navigatorService.fireSelectedNodeChanged(node),
error => this.notificationService.notifyError(this.translateService.instant('tableview.tableview.err-cannot-calculate-node'), error)
);
let event: MouseEvent = e.originalEvent;
if (event.shiftKey && this.selectedRows.length > 0) {
let lastRow = this.selectedRows[this.selectedRows.length - 1];
let lastIndex = this.results.rows.findIndex(r => r.equals(lastRow));
let thisIndex = this.results.rows.findIndex(r => r.equals(row));
if (this.selectedRows.findIndex(sr => sr.equals(row)) > -1) {
} else {
let min = Math.min(lastIndex, thisIndex);
let max = Math.max(lastIndex, thisIndex);
this.results.rows.slice(min, max + 1)
.forEach(r => {
if (this.selectedRows.findIndex(sr => sr.equals(r)) === -1) {
this.selectedRows.push(r);
}
});
}
} else if (event.ctrlKey) {
this.selectRow(row);
} else {
if (this.selectedRows.length > 1 || (this.selectedRows.length !== 0 && !row.equals(this.selectedRows[0]))) {
this.selectedRows = [];
}
this.selectRow(row);
}
}
/** @todo: helping function for multi select in p-datatable. p-table does not support select mode
* multiple with check box. Thus, this is not used at the moment. Might be kept as guide line in case
* future version of p-table support checkbox with multi selection.
* @deprecated
**/
selectRow(row: Row) {
let index = this.selectedRows.findIndex(ai => ai.equals(row));
if (index === -1) {
this.selectedRows.push(row);
} else {
this.selectedRows.splice(index, 1);
}
}
openInTree(row?: Row) {
if (row != undefined) {
this.selectedViewRows = [row];
}
if (this.selectedViewRows != undefined && this.selectedViewRows.length === 0 && this.menuSelectedRow != undefined) {
this.navigatorService.fireOnOpenInTree([this.menuSelectedRow.getItem()]);
} else if (row) {
this.navigatorService.fireOnOpenInTree([row.getItem()]);
} else {
this.navigatorService.fireOnOpenInTree(this.selectedRows.map(r => r.getItem()));
}
}
}