Added in panel support - details panels
Change-Id: I2803edd6fe12cb0d97a2d3c45a692ea701786dd2
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
new file mode 100644
index 0000000..0093f72
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.base.ts
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { FnService } from '../util/fn.service';
+import { LoadingService } from '../layer/loading.service';
+import { LogService } from '../../log.service';
+import { WebSocketService } from '../remote/websocket.service';
+import { Observable, of } from 'rxjs';
+
+const REFRESH_INTERVAL = 2000;
+const SEARCH_REGEX = '\\W';
+
+/**
+ * Model of table annotations within this table base class
+ */
+interface TableAnnots {
+ noRowsMsg: string;
+}
+
+/**
+ * A model of data returned from Web Socket in a TableResponse
+ *
+ * There is an interface extending from this one in the parent component
+ */
+export interface TableResponse {
+ annots: any;
+ // There will be other parts to the response depending on table type
+ // Expect one called tag+'s' e.g. devices or apps
+}
+
+/**
+ * A criteria for filtering the tableData
+ */
+export interface TableFilter {
+ queryStr: string;
+ queryBy: string;
+ sortBy: string;
+}
+
+/**
+ * Enumerated values for the sort dir
+ */
+export enum SortDir {
+ asc = 'asc', desc = 'desc'
+}
+
+/**
+ * A structure to format sort params for table
+ * This is sent to WebSocket as part of table request
+ */
+export interface SortParams {
+ firstCol: string;
+ firstDir: SortDir;
+ secondCol: string;
+ secondDir: SortDir;
+}
+
+/**
+ * ONOS GUI -- Widget -- Table Base class
+ */
+export abstract class TableBaseImpl {
+ // attributes from the interface
+ protected annots: TableAnnots;
+ protected changedData: string[] = [];
+ protected payloadParams: any;
+ protected sortParams: SortParams;
+ protected selectCallback; // Function
+ protected parentSelCb = null;
+ protected responseCallback; // Function
+ selId: string = undefined;
+ tableData: any[] = [];
+ tableDataFilter: TableFilter;
+ toggleRefresh; // Function
+ autoRefresh: boolean = true;
+ autoRefreshTip: string = 'Toggle auto refresh'; // TODO: get LION string
+
+ private root: string;
+ private req: string;
+ private resp: string;
+ private refreshPromise: any = null;
+ private handlers: string[] = [];
+
+ constructor(
+ protected fs: FnService,
+ protected ls: LoadingService,
+ protected log: LogService,
+ protected wss: WebSocketService,
+ protected tag: string,
+ protected idKey: string = 'id',
+ protected selCb = () => ({}) // Function
+ ) {
+ this.root = tag + 's';
+ this.req = tag + 'DataRequest';
+ this.resp = tag + 'DataResponse';
+
+ this.selectCallback = this.rowSelectionCb;
+ this.toggleRefresh = () => {
+ this.autoRefresh = !this.autoRefresh;
+ this.autoRefresh ? this.startRefresh() : this.stopRefresh();
+ };
+
+ // Mapped to the search and searchBy inputs in template
+ // Changes are handled through TableFilterPipe
+ this.tableDataFilter = <TableFilter>{
+ queryStr: '',
+ queryBy: '$',
+ };
+ }
+
+ init() {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ [this.resp, (data) => this.tableDataResponseCb(data)]
+ ]));
+ this.handlers.push(this.resp);
+
+ this.annots = <TableAnnots>{
+ noRowsMsg: ''
+ };
+
+ // Now send the WebSocket request and make it repeat every 2 seconds
+ this.requestTableData();
+ this.startRefresh();
+ this.log.debug('TableBase initialized. Calling ', this.req,
+ 'every', REFRESH_INTERVAL, 'ms');
+ }
+
+ destroy() {
+ this.wss.unbindHandlers(this.handlers);
+ this.stopRefresh();
+ this.ls.stop();
+ }
+
+ /**
+ * A callback that executes when the table data that was requested
+ * on WebSocketService arrives.
+ *
+ * Happens every 2 seconds
+ */
+ tableDataResponseCb(data: TableResponse) {
+ this.ls.stop();
+
+ const newTableData: any[] = Array.from(data[this.root]);
+ this.annots.noRowsMsg = data.annots.no_rows_msg;
+
+ // If the parents onResp() function is set then call it
+ if (this.responseCallback) {
+ this.responseCallback(data);
+ }
+ this.changedData = [];
+
+ // checks if data changed for row flashing
+ 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
+ // only flash the row if the data already exists
+ if (oldTableData.length > 0) {
+ for (const idx in newTableData) {
+ if (!this.fs.containsObj(oldTableData, newTableData[idx])) {
+ this.changedData.push(newTableData[idx][this.idKey]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Table Data Request
+ * Pass in sort parameters and the set will be returned sorted
+ * In the old GUI there was also a query parameter, but this was not
+ * implemented on the server end
+ */
+ requestTableData() {
+ const p = Object.assign({}, this.sortParams, this.payloadParams);
+
+ // Allow it to sit in pending events
+ if (this.wss.isConnected()) {
+ if (this.fs.debugOn('table')) {
+ this.log.debug('Table data REQUEST:', this.req, p);
+ }
+ this.wss.sendEvent(this.req, p);
+ this.ls.start();
+ }
+ }
+
+ /**
+ * Row Selected
+ */
+ rowSelectionCb(event: any, selRow: any): void {
+ const selId: string = selRow[this.idKey];
+ this.selId = (this.selId === selId) ? undefined : selId;
+ this.log.debug('Row', selId, 'selected');
+ if (this.parentSelCb) {
+ this.parentSelCb(event, selRow);
+ }
+ }
+
+ /**
+ * autoRefresh functions
+ */
+ startRefresh() {
+ this.refreshPromise =
+ setInterval(() => {
+ if (!this.ls.waiting()) {
+ if (this.fs.debugOn('table')) {
+ this.log.debug('Refreshing ' + this.root + ' page');
+ }
+ this.requestTableData();
+ }
+ }, REFRESH_INTERVAL);
+ }
+
+ stopRefresh() {
+ if (this.refreshPromise) {
+ clearInterval(this.refreshPromise);
+ this.refreshPromise = null;
+ }
+ }
+
+ isChanged(id: string): boolean {
+ return (this.fs.inArray(id, this.changedData) === -1) ? false : true;
+ }
+
+ /**
+ * A dummy implementation of the lionFn until the response is received and the LION
+ * bundle is received from the WebSocket
+ */
+ dummyLion(key: string): string {
+ return '%' + key + '%';
+ }
+
+ /**
+ * Change the sort order of the data returned
+ *
+ * sortParams are passed to the server by WebSocket and the data is
+ * returned sorted
+ *
+ * This is usually assigned to the (click) event on a column, and the column
+ * name passed in e.g. (click)="onSort('origin')
+ * If the column that is passed in is already the firstCol, then reverse its direction
+ * If a new column is passed in, then make the existing col the 2nd sort order
+ */
+ onSort(colName: string) {
+ if (this.sortParams.firstCol === colName) {
+ if (this.sortParams.firstDir === SortDir.desc) {
+ this.sortParams.firstDir = SortDir.asc;
+ return;
+ } else {
+ this.sortParams.firstDir = SortDir.desc;
+ return;
+ }
+ } else {
+ this.sortParams.secondCol = this.sortParams.firstCol;
+ this.sortParams.secondDir = this.sortParams.firstDir;
+ this.sortParams.firstCol = colName;
+ this.sortParams.firstDir = SortDir.desc;
+ }
+ this.log.debug('Sort params', this.sortParams);
+ this.requestTableData();
+ }
+
+ sortIcon(column: string): string {
+ if (this.sortParams.firstCol === column) {
+ if (this.sortParams.firstDir === SortDir.asc) {
+ return 'upArrow';
+ } else {
+ return 'downArrow';
+ }
+ } else {
+ return '';
+ }
+ }
+}