| /******************************************************************************** |
| * 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())); |
| } |
| } |
| } |