GUI2 implementation of device/flow/port/group/meter/host/link/tunnel view

Review comments incorporated.

Change-Id: I45dd6570961cc3e0f4ffddb7acbf02cd7d860de5
diff --git a/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts b/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts
index d7cf7b8..59f85c5 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts
@@ -19,6 +19,7 @@
 import { WebSocketService } from '../remote/websocket.service';
 
 import { PanelBaseImpl } from './panel.base';
+import { Output, EventEmitter, Input } from '@angular/core';
 
 /**
  * A generic model of the data returned from the *DetailsResponse
@@ -37,6 +38,9 @@
  */
 export abstract class DetailsPanelBaseImpl extends PanelBaseImpl {
 
+    @Input() id: string;
+    @Output() closeEvent = new EventEmitter<string>();
+
     private root: string;
     private req: string;
     private resp: string;
@@ -85,16 +89,10 @@
     }
 
     /**
-     * Details Panel Data Request - should be called whenever id changes
-     * If id is empty, no request is made
+     * Details Panel Data Request - should be called whenever row id changes
      */
-    requestDetailsPanelData(id: string) {
-        if (id === '') {
-            return;
-        }
+    requestDetailsPanelData(query: any) {
         this.closed = false;
-        const query = {'id': id};
-
         // Do not send if the Web Socket hasn't opened
         if (this.wss.isConnected()) {
             if (this.fs.debugOn('panel')) {
@@ -109,5 +107,8 @@
      */
     close(): void {
         this.closed = true;
+        this.id = null;
+        this.closeEvent.emit(this.id);
     }
+
 }
diff --git a/web/gui2/src/main/webapp/app/fw/widget/panel.css b/web/gui2/src/main/webapp/app/fw/widget/panel.css
index 34d127f..48530ac 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/panel.css
+++ b/web/gui2/src/main/webapp/app/fw/widget/panel.css
@@ -22,9 +22,9 @@
     position: absolute;
     z-index: 100;
     display: block;
-    top: 120px;
-    width: 500px;
-    right: -505px;
+    top: 160px;
+    width: 544px;
+    right: -550px;
     opacity: 100;
 
     padding: 2px;
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table.base.ts b/web/gui2/src/main/webapp/app/fw/widget/table.base.ts
index 0093f72..cc29878 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/table.base.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.base.ts
@@ -67,6 +67,11 @@
     secondDir: SortDir;
 }
 
+export interface PayloadParams {
+    devId: string;
+}
+
+
 /**
  * ONOS GUI -- Widget -- Table Base class
  */
@@ -74,7 +79,7 @@
     // attributes from the interface
     protected annots: TableAnnots;
     protected changedData: string[] = [];
-    protected payloadParams: any;
+    protected payloadParams: PayloadParams;
     protected sortParams: SortParams;
     protected selectCallback; // Function
     protected parentSelCb = null;
@@ -164,7 +169,7 @@
         if (JSON.stringify(newTableData) !== JSON.stringify(this.tableData)) {
             this.log.debug('table data has changed');
             const oldTableData: any[] = this.tableData;
-            this.tableData = [ ...newTableData ]; // ES6 spread syntax
+            this.tableData = [...newTableData]; // ES6 spread syntax
             // only flash the row if the data already exists
             if (oldTableData.length > 0) {
                 for (const idx in newTableData) {
@@ -282,4 +287,12 @@
             return '';
         }
     }
+
+    /**
+     * De-selects the row
+     */
+    deselectRow(event) {
+        this.log.debug('Details panel close event');
+        this.selId = event;
+    }
 }
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table.css b/web/gui2/src/main/webapp/app/fw/widget/table.css
index 9df99ef..1ed43b4 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/table.css
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.css
@@ -31,14 +31,13 @@
 
 div.summary-list div.table-body {
     overflow-y: scroll;
-    max-height:70vh;
 }
 
 div.summary-list div.table-body::-webkit-scrollbar {
     display: none;
 }
 
-div.summary-list tr.no-data td {
+div.summary-list div.table-body tr.no-data td {
     text-align: center;
     font-style: italic;
 }
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts b/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts
index fef5123..21ff59c 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts
@@ -13,9 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Directive, ElementRef } from '@angular/core';
+import { AfterContentChecked, Directive, ElementRef, Inject } from '@angular/core';
 import { FnService } from '../util/fn.service';
 import { LogService } from '../../log.service';
+import { MastService } from '../mast/mast.service';
+import { HostListener } from '@angular/core';
+import * as d3 from 'd3';
 
 /**
  * ONOS GUI -- Widget -- Table Resize Directive
@@ -23,20 +26,58 @@
 @Directive({
     selector: '[onosTableResize]',
 })
-export class TableResizeDirective {
+export class TableResizeDirective implements AfterContentChecked {
 
-    constructor(
-        private fs: FnService,
-        public log: LogService,
-        private el: ElementRef,
-    ) {
+    pdg = 22;
+    tables: any;
 
-        this.windowSize();
-        this.log.debug('TableResizeDirective constructed');
+    constructor(protected fs: FnService,
+        protected log: LogService,
+        protected mast: MastService,
+        protected el: ElementRef,
+        @Inject('Window') private w: Window) {
+
+        log.info('TableResizeDirective constructed');
     }
 
-    windowSize() {
+    ngAfterContentChecked() {
+        this.tables = {
+            thead: d3.select('div.table-header').select('table'),
+            tbody: d3.select('div.table-body').select('table')
+        };
+        this.windowSize(this.tables);
+    }
+
+    windowSize(tables: any) {
         const wsz = this.fs.windowSize(0, 30);
-        this.el.nativeElement.style.width = wsz.width + 'px';
+        this.adjustTable(tables, wsz.width, wsz.height);
     }
+
+    @HostListener('window:resize', ['event'])
+    onResize(event: any) {
+        this.windowSize(this.tables);
+        return {
+            h: this.w.innerHeight,
+            w: this.w.innerWidth
+        };
+    }
+
+    adjustTable(tables: any, width: number, height: number) {
+        this._width(tables.thead, width + 'px');
+        this._width(tables.tbody, width + 'px');
+
+        this.setHeight(tables.thead, d3.select('div.table-body'), height);
+    }
+
+    _width(elem, width) {
+        elem.style('width', width);
+    }
+
+    setHeight(thead: any, body: any, height: number) {
+        const h = height - (this.mast.mastHeight +
+            this.fs.noPxStyle(d3.select('.tabular-header'), 'height') +
+            this.fs.noPxStyle(thead, 'height') + this.pdg);
+        body.style('height', h + 'px');
+    }
+
 }