]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard_v2: move datatable module
authorTiago Melo <tmelo@suse.com>
Wed, 14 Feb 2018 15:49:39 +0000 (15:49 +0000)
committerRicardo Dias <rdias@suse.com>
Mon, 5 Mar 2018 13:07:10 +0000 (13:07 +0000)
By moving the datatable module outside of the  components module we
can avoid a circular dependency between the datatable and sparkline
component.

Signed-off-by: Tiago Melo <tmelo@suse.com>
25 files changed:
src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.spec.ts
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/datatable.module.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-details.directive.spec.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-details.directive.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.html [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.scss [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.spec.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.html [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.scss [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.spec.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.ts [deleted file]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/datatable.module.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-details.directive.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-details.directive.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/frontend/src/app/shared/shared.module.ts

index cc76da0f1132d8a084a97dbe0d7e052bb72c414d..5deca8c72e243481ffaf24aed223215b456ad49c 100644 (file)
@@ -2,7 +2,7 @@ import { HttpClientModule } from '@angular/common/http';
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { ComponentsModule } from '../../../shared/components/components.module';
+import { DataTableModule } from '../../../shared/datatable/datatable.module';
 import { RgwDaemonService } from '../services/rgw-daemon.service';
 import { RgwDaemonListComponent } from './rgw-daemon-list.component';
 
@@ -14,7 +14,7 @@ describe('RgwDaemonListComponent', () => {
     TestBed.configureTestingModule({
       declarations: [ RgwDaemonListComponent ],
       imports: [
-        ComponentsModule,
+        DataTableModule,
         HttpClientTestingModule,
         HttpClientModule
       ],
index 0ea1f4397cd4fd14076bb913e25c5f2b5edd7695..fea298f6d506283e6ff00cb3d00bc16b9d4f91ff 100644 (file)
@@ -1,21 +1,20 @@
 import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 
+import { ChartsModule } from 'ng2-charts/ng2-charts';
 import { AlertModule } from 'ngx-bootstrap';
-import { DataTableModule } from './datatable/datatable.module';
 
 import { ViewCacheComponent } from './view-cache/view-cache.component';
 
 @NgModule({
   imports: [
     CommonModule,
-    DataTableModule,
-    AlertModule.forRoot()
+    AlertModule.forRoot(),
+    ChartsModule
   ],
   declarations: [ViewCacheComponent],
   providers: [],
   exports: [
-    DataTableModule,
     ViewCacheComponent
   ]
 })
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/datatable.module.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/datatable.module.ts
deleted file mode 100644 (file)
index f0c6f75..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { NgModule } from '@angular/core';
-import { FormsModule } from '@angular/forms';
-
-import { NgxDatatableModule } from '@swimlane/ngx-datatable';
-
-import { PipesModule } from '../../pipes/pipes.module';
-import { TableDetailsDirective } from './table-details.directive';
-import { TableKeyValueComponent } from './table-key-value/table-key-value.component';
-import { TableComponent } from './table/table.component';
-
-@NgModule({
-  imports: [
-    CommonModule,
-    NgxDatatableModule,
-    FormsModule,
-    PipesModule
-  ],
-  declarations: [
-    TableComponent,
-    TableDetailsDirective,
-    TableKeyValueComponent
-  ],
-  exports: [
-    TableComponent,
-    NgxDatatableModule,
-    TableKeyValueComponent
-  ]
-})
-export class DataTableModule { }
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-details.directive.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-details.directive.spec.ts
deleted file mode 100644 (file)
index b3e2684..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import { TableDetailsDirective } from './table-details.directive';
-
-describe('TableDetailsDirective', () => {
-  it('should create an instance', () => {
-    const directive = new TableDetailsDirective(null);
-    expect(directive).toBeTruthy();
-  });
-});
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-details.directive.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-details.directive.ts
deleted file mode 100644 (file)
index 5c529dc..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Directive, Input, ViewContainerRef } from '@angular/core';
-
-@Directive({
-  selector: '[cdTableDetails]'
-})
-export class TableDetailsDirective {
-  @Input() selected?: any[];
-
-  constructor(public viewContainerRef: ViewContainerRef) { }
-
-}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.html b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.html
deleted file mode 100644 (file)
index ad5346f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<cd-table [data]="data"
-          [columns]="columns"
-          columnMode="flex"
-          [toolHeader]="false"
-          [header]="false"
-          [footer]="false"
-          [limit]="0">
-</cd-table>
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.scss b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.spec.ts
deleted file mode 100644 (file)
index a116866..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { FormsModule } from '@angular/forms';
-
-import { NgxDatatableModule } from '@swimlane/ngx-datatable';
-
-import { TableComponent } from '../table/table.component';
-import { TableKeyValueComponent } from './table-key-value.component';
-
-describe('TableKeyValueComponent', () => {
-  let component: TableKeyValueComponent;
-  let fixture: ComponentFixture<TableKeyValueComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ TableComponent, TableKeyValueComponent ],
-      imports: [ FormsModule, NgxDatatableModule ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(TableKeyValueComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table-key-value/table-key-value.component.ts
deleted file mode 100644 (file)
index 294c34f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-
-import { CellTemplate } from '../../../enum/cell-template.enum';
-import { CdTableColumn } from '../../../models/cd-table-column';
-
-/**
- * Display the given data in a 2 column data table. The left column
- * shows the 'key' attribute, the right column the 'value' attribute.
- * The data table has the following characteristics:
- * - No header and footer is displayed
- * - The relation of the width for the columns 'key' and 'value' is 1:3
- * - The 'key' column is displayed in bold text
- */
-@Component({
-  selector: 'cd-table-key-value',
-  templateUrl: './table-key-value.component.html',
-  styleUrls: ['./table-key-value.component.scss']
-})
-export class TableKeyValueComponent implements OnInit {
-
-  columns: Array<CdTableColumn> = [];
-
-  /**
-   * An array of objects to be displayed in the data table.
-   */
-  @Input() data: Array<object> = [];
-
-  /**
-   * The name of the attribute to be displayed as key.
-   * Defaults to 'key'.
-   * @type {string}
-   */
-  @Input() key = 'key';
-
-  /**
-   * The name of the attribute to be displayed as value.
-   * Defaults to 'value'.
-   * @type {string}
-   */
-  @Input() value = 'value';
-
-  constructor() { }
-
-  ngOnInit() {
-    this.columns = [
-      {
-        prop: this.key,
-        flexGrow: 1,
-        cellTransformation: CellTemplate.bold
-      },
-      {
-        prop: this.value,
-        flexGrow: 3
-      }
-    ];
-  }
-}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.html b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.html
deleted file mode 100644 (file)
index f51aa6f..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-<div class="dataTables_wrapper">
-  <div class="dataTables_header clearfix"
-       *ngIf="toolHeader">
-    <!-- actions -->
-    <div class="oadatatableactions">
-        <ng-content select="table-actions"></ng-content>
-    </div>
-    <!-- end actions -->
-
-    <!-- search -->
-    <div class="input-group">
-      <span class="input-group-addon">
-        <i class="glyphicon glyphicon-search"></i>
-      </span>
-      <input class="form-control"
-             type="text"
-             [(ngModel)]="search"
-             (keyup)='updateFilter($event)'>
-      <span class="input-group-btn">
-        <button type="button"
-                class="btn btn-default clear-input tc_clearInputBtn"
-                (click)="updateFilter()">
-          <i class="icon-prepend fa fa-remove"></i>
-        </button>
-      </span>
-    </div>
-    <!-- end search -->
-
-    <!-- pagination limit -->
-    <div class="input-group dataTables_paginate">
-      <input class="form-control"
-             type="number"
-             min="1"
-             max="9999"
-             [value]="limit"
-             (click)="setLimit($event)"
-             (keyup)="setLimit($event)"
-             (blur)="setLimit($event)">
-    </div>
-    <!-- end pagination limit-->
-
-    <!-- refresh button -->
-    <div class="widget-toolbar tc_refreshBtn">
-      <a (click)="reloadData()">
-        <i class="fa fa-lg fa-refresh"></i>
-      </a>
-    </div>
-    <!-- end refresh button -->
-  </div>
-  <ngx-datatable #table
-                 class="bootstrap oadatatable"
-                 [cssClasses]="paginationClasses"
-                 [selectionType]="selectionType"
-                 [selected]="selected"
-                 (select)="toggleExpandRow()"
-                 [columns]="columns"
-                 [columnMode]="columnMode"
-                 [rows]="rows"
-                 [rowClass]="getRowClass()"
-                 [headerHeight]="header ? 'auto' : 0"
-                 [footerHeight]="footer ? 'auto' : 0"
-                 [limit]="limit > 0 ? limit : undefined"
-                 [loadingIndicator]="true"
-                 [rowHeight]="'auto'">
-    <!-- Row Detail Template -->
-    <ngx-datatable-row-detail (toggle)="updateDetailView()">
-    </ngx-datatable-row-detail>
-  </ngx-datatable>
-</div>
-<ng-template cdTableDetails></ng-template>
-<!-- cell templates that can be accessed from outside -->
-<ng-template #tableCellBoldTpl
-             let-row="row"
-             let-value="value">
-  <strong>{{ value }}</strong>
-</ng-template>
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.scss b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.scss
deleted file mode 100644 (file)
index ca5ad6f..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-@import '../../../../../defaults';
-
-.dataTables_wrapper {
-  margin-bottom: 25px;
-  .separator {
-    height: 30px;
-    border-left: 1px solid rgba(0,0,0,.09);
-    padding-left: 5px;
-    margin-left: 5px;
-    display: inline-block;
-    vertical-align: middle;
-  }
-  .widget-toolbar {
-    display: inline-block;
-    float: right;
-    width: auto;
-    height: 30px;
-    line-height: 28px;
-    position: relative;
-    border-left: 1px solid rgba(0,0,0,.09);
-    cursor: pointer;
-    padding: 0 8px;
-    text-align: center;
-  }
-  .dropdown-menu {
-    white-space: nowrap;
-    & > li {
-      cursor: pointer;
-      & > label {
-        width: 100%;
-        margin-bottom: 0;
-        padding-left: 20px;
-        padding-right: 20px;
-        cursor: pointer;
-        &:hover {
-          background-color: #f5f5f5;
-        }
-        & > input {
-          cursor: pointer;
-        }
-      }
-    }
-  }
-  th.oadatatablecheckbox {
-    width: 16px;
-  }
-  .dataTables_length>input {
-    line-height: 25px;
-    text-align: right;
-  }
-}
-.dataTables_header {
-  background-color: #f6f6f6;
-  border: 1px solid #d1d1d1;
-  border-bottom: none;
-  padding: 5px;
-  position: relative;
-  .oadatatableactions {
-    display: inline-block;
-  }
-  .input-group {
-    float: right;
-    border-left: 1px solid rgba(0,0,0,.09);
-    padding-left: 8px;
-    width: 40%;
-    max-width: 350px;
-    .form-control {
-      height: 30px;
-    }
-    .clear-input {
-      height: 30px;
-      i {
-        vertical-align: text-top;
-      }
-    }
-  }
-  .input-group.dataTables_paginate {
-    width: 8%;
-  }
-}
-
-::ng-deep .oadatatable {
-  border: $border-color;
-  margin-bottom: 0;
-  max-width: none!important;
-  .datatable-header {
-    background-clip: padding-box;
-    background-color: #f9f9f9;
-    background-image: -webkit-linear-gradient(top,#fafafa 0,#ededed 100%);
-    background-image: -o-linear-gradient(top,#fafafa 0,#ededed 100%);
-    background-image: linear-gradient(to bottom,#fafafa 0,#ededed 100%);
-    background-repeat: repeat-x;
-    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffafafa', endColorstr='#ffededed', GradientType=0);
-    .sort-asc, .sort-desc {
-      color: $oa-color-blue;
-    }
-    .datatable-header-cell{
-      @include table-cell;
-      text-align: left;
-      font-weight: bold;
-      .datatable-header-cell-label {
-        &:after {
-          font-family: FontAwesome;
-          font-weight: 400;
-          height: 9px;
-          left: 10px;
-          line-height: 12px;
-          position: relative;
-          vertical-align: baseline;
-          width: 12px;
-        }
-      }
-      &.sortable {
-        .datatable-header-cell-label:after {
-          content: " \f0dc";
-        }
-        &.sort-active {
-          &.sort-asc .datatable-header-cell-label:after {
-            content: " \f160";
-          }
-          &.sort-desc .datatable-header-cell-label:after {
-            content: " \f161";
-          }
-        }
-      }
-      &:first-child {
-        border-left: none;
-      }
-    }
-  }
-  .datatable-body {
-    .datatable-body-row {
-      &.clickable:hover .datatable-row-group {
-        background-color: #eee;
-        transition-property: background;
-        transition-duration: .3s;
-        transition-timing-function: linear;
-      }
-      &.datatable-row-even {
-        background-color: #ffffff;
-      }
-      &.datatable-row-odd {
-        background-color: #f6f6f6;
-      }
-      &.active, &.active:hover {
-        background-color: $bg-color-light-blue;
-      }
-      .datatable-body-cell{
-        @include table-cell;
-        &:first-child {
-          border-left: none;
-        }
-        .datatable-body-cell-label {
-          display: block;
-        }
-      }
-    }
-  }
-  .datatable-footer {
-    .selected-count, .page-count {
-      font-style: italic;
-      padding-left: 5px;
-    }
-    .datatable-pager .pager {
-      margin-right: 5px;
-      .pages {
-        & > a, & > span {
-          display: inline-block;
-          padding: 5px 10px;
-          margin-bottom: 5px;
-          border: none;
-        }
-        a:hover {
-          background-color: $oa-color-light-blue;
-        }
-        &.active > a {
-          background-color: $bg-color-light-blue;
-        }
-      }
-    }
-  }
-}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.spec.ts
deleted file mode 100644 (file)
index 07a1825..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { FormsModule } from '@angular/forms';
-
-import { NgxDatatableModule, TableColumn } from '@swimlane/ngx-datatable';
-
-import { TableComponent } from './table.component';
-
-describe('TableComponent', () => {
-  let component: TableComponent;
-  let fixture: ComponentFixture<TableComponent>;
-  const columns: TableColumn[] = [];
-  const createFakeData = (n) => {
-    const data = [];
-    for (let i = 0; i < n; i++) {
-      data.push({
-        a: i,
-        b: i * i,
-        c: -(i % 10)
-      });
-    }
-    return data;
-  };
-
-  beforeEach(
-    async(() => {
-      TestBed.configureTestingModule({
-        declarations: [TableComponent],
-        imports: [NgxDatatableModule, FormsModule]
-      }).compileComponents();
-    })
-  );
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(TableComponent);
-    component = fixture.componentInstance;
-  });
-
-  beforeEach(() => {
-    component.data = createFakeData(100);
-    component.useData();
-    component.columns = [
-      {prop: 'a'},
-      {prop: 'b'},
-      {prop: 'c'}
-    ];
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-
-  it('should have rows', () => {
-    expect(component.data.length).toBe(100);
-    expect(component.rows.length).toBe(component.data.length);
-  });
-
-  it('should have an int in setLimit parsing a string', () => {
-    expect(component.limit).toBe(10);
-    expect(component.limit).toEqual(jasmine.any(Number));
-
-    const e = {target: {value: '1'}};
-    component.setLimit(e);
-    expect(component.limit).toBe(1);
-    expect(component.limit).toEqual(jasmine.any(Number));
-    e.target.value = '-20';
-    component.setLimit(e);
-    expect(component.limit).toBe(1);
-  });
-
-  it('should search for 13', () => {
-    component.search = '13';
-    expect(component.rows.length).toBe(100);
-    component.updateFilter(true);
-    expect(component.rows[0].a).toBe(13);
-    expect(component.rows[1].b).toBe(1369);
-    expect(component.rows[2].b).toBe(3136);
-    expect(component.rows.length).toBe(3);
-  });
-
-  it('should restore full table after search', () => {
-    component.search = '13';
-    expect(component.rows.length).toBe(100);
-    component.updateFilter(true);
-    expect(component.rows.length).toBe(3);
-    component.updateFilter();
-    expect(component.rows.length).toBe(100);
-  });
-});
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/components/datatable/table/table.component.ts
deleted file mode 100644 (file)
index d2c522e..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-import {
-  AfterContentChecked,
-  Component,
-  ComponentFactoryResolver,
-  EventEmitter,
-  Input,
-  OnChanges,
-  OnInit,
-  Output,
-  TemplateRef,
-  Type,
-  ViewChild
-} from '@angular/core';
-
-import { DatatableComponent } from '@swimlane/ngx-datatable';
-import * as _ from 'lodash';
-
-import { CdTableColumn } from '../../../models/cd-table-column';
-import { TableDetailsDirective } from '../table-details.directive';
-
-@Component({
-  selector: 'cd-table',
-  templateUrl: './table.component.html',
-  styleUrls: ['./table.component.scss']
-})
-export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
-  @ViewChild(DatatableComponent) table: DatatableComponent;
-  @ViewChild(TableDetailsDirective) detailTemplate: TableDetailsDirective;
-  @ViewChild('tableCellBoldTpl') tableCellBoldTpl: TemplateRef<any>;
-
-  // This is the array with the items to be shown.
-  @Input() data: any[];
-  // Each item -> { prop: 'attribute name', name: 'display name' }
-  @Input() columns: CdTableColumn[];
-  // Method used for setting column widths.
-  @Input() columnMode ?= 'force';
-  // Name of the component e.g. 'TableDetailsComponent'
-  @Input() detailsComponent?: string;
-  // Display the tool header, including reload button, pagination and search fields?
-  @Input() toolHeader ?= true;
-  // Display the table header?
-  @Input() header ?= true;
-  // Display the table footer?
-  @Input() footer ?= true;
-  // Page size to show. Set to 0 to show unlimited number of rows.
-  @Input() limit ?= 10;
-  // An optional function that is called before the details page is show.
-  // The current selection is passed as function argument. To do not display
-  // the details page, return false.
-  @Input() beforeShowDetails: Function;
-  // Should be the function that will update the input data.
-  @Output() fetchData = new EventEmitter();
-
-  cellTemplates: {
-    [key: string]: TemplateRef<any>
-  } = {};
-  selectionType: string = undefined;
-  search = '';
-  rows = [];
-  selected = [];
-  paginationClasses = {
-    pagerLeftArrow: 'i fa fa-angle-double-left',
-    pagerRightArrow: 'i fa fa-angle-double-right',
-    pagerPrevious: 'i fa fa-angle-left',
-    pagerNext: 'i fa fa-angle-right'
-  };
-
-  // Internal variable to check if it is necessary to recalculate the
-  // table columns after the browser window has been resized.
-  private currentWidth: number;
-
-  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
-
-  ngOnInit() {
-    this._addTemplates();
-    this.columns.map((column) => {
-      if (column.cellTransformation) {
-        column.cellTemplate = this.cellTemplates[column.cellTransformation];
-      }
-      return column;
-    });
-    this.reloadData();
-    if (this.detailsComponent) {
-      this.selectionType = 'multi';
-    }
-  }
-
-  ngAfterContentChecked() {
-    // If the data table is not visible, e.g. another tab is active, and the
-    // browser window gets resized, the table and its columns won't get resized
-    // automatically if the tab gets visible again.
-    // https://github.com/swimlane/ngx-datatable/issues/193
-    // https://github.com/swimlane/ngx-datatable/issues/193#issuecomment-329144543
-    if (this.table && this.table.element.clientWidth !== this.currentWidth) {
-      this.currentWidth = this.table.element.clientWidth;
-      // Force the redrawing of the table.
-      window.dispatchEvent(new Event('resize'));
-    }
-  }
-
-  _addTemplates () {
-    this.cellTemplates.bold = this.tableCellBoldTpl;
-  }
-
-  ngOnChanges(changes) {
-    this.useData();
-  }
-
-  setLimit(e) {
-    const value = parseInt(e.target.value, 10);
-    if (value > 0) {
-      this.limit = value;
-    }
-  }
-
-  reloadData() {
-    this.fetchData.emit();
-  }
-
-  useData() {
-    this.rows = [...this.data];
-  }
-
-  toggleExpandRow() {
-    if (this.selected.length > 0) {
-      this.table.rowDetail.toggleExpandRow(this.selected[0]);
-    } else {
-      this.detailTemplate.viewContainerRef.clear();
-    }
-  }
-
-  updateDetailView() {
-    if (!this.detailsComponent) {
-      return;
-    }
-    if (_.isFunction(this.beforeShowDetails)) {
-      if (!this.beforeShowDetails(this.selected)) {
-        return;
-      }
-    }
-    const factories = Array.from(this.componentFactoryResolver['_factories'].keys());
-    const factoryClass = <Type<any>>factories.find((x: any) => x.name === this.detailsComponent);
-    this.detailTemplate.viewContainerRef.clear();
-    const cmpRef = this.detailTemplate.viewContainerRef.createComponent(
-      this.componentFactoryResolver.resolveComponentFactory(factoryClass)
-    );
-    cmpRef.instance.selected = this.selected;
-  }
-
-  updateFilter(event?) {
-    if (!event) {
-      this.search = '';
-    }
-    const val = this.search.toLowerCase();
-    const columns = this.columns;
-    // update the rows
-    this.rows = this.data.filter(function (d) {
-      return columns.filter((c) => {
-        return (typeof d[c.prop] === 'string' || typeof d[c.prop] === 'number')
-          && (d[c.prop] + '').toLowerCase().indexOf(val) !== -1;
-      }).length > 0;
-    });
-    // Whenever the filter changes, always go back to the first page
-    this.table.offset = 0;
-  }
-
-  getRowClass() {
-    // Return the function used to populate a row's CSS classes.
-    return () => {
-      return {
-        'clickable': !_.isUndefined(this.detailsComponent)
-      };
-    };
-  }
-}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/datatable.module.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/datatable.module.ts
new file mode 100644 (file)
index 0000000..1c3b870
--- /dev/null
@@ -0,0 +1,32 @@
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+
+import { NgxDatatableModule } from '@swimlane/ngx-datatable';
+
+import { ComponentsModule } from '../components/components.module';
+import { PipesModule } from '../pipes/pipes.module';
+import { TableDetailsDirective } from './table-details.directive';
+import { TableKeyValueComponent } from './table-key-value/table-key-value.component';
+import { TableComponent } from './table/table.component';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    NgxDatatableModule,
+    FormsModule,
+    PipesModule,
+    ComponentsModule
+  ],
+  declarations: [
+    TableComponent,
+    TableDetailsDirective,
+    TableKeyValueComponent
+  ],
+  exports: [
+    TableComponent,
+    NgxDatatableModule,
+    TableKeyValueComponent
+  ]
+})
+export class DataTableModule { }
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-details.directive.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-details.directive.spec.ts
new file mode 100644 (file)
index 0000000..b3e2684
--- /dev/null
@@ -0,0 +1,8 @@
+import { TableDetailsDirective } from './table-details.directive';
+
+describe('TableDetailsDirective', () => {
+  it('should create an instance', () => {
+    const directive = new TableDetailsDirective(null);
+    expect(directive).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-details.directive.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-details.directive.ts
new file mode 100644 (file)
index 0000000..5c529dc
--- /dev/null
@@ -0,0 +1,11 @@
+import { Directive, Input, ViewContainerRef } from '@angular/core';
+
+@Directive({
+  selector: '[cdTableDetails]'
+})
+export class TableDetailsDirective {
+  @Input() selected?: any[];
+
+  constructor(public viewContainerRef: ViewContainerRef) { }
+
+}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.html b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.html
new file mode 100644 (file)
index 0000000..ad5346f
--- /dev/null
@@ -0,0 +1,8 @@
+<cd-table [data]="data"
+          [columns]="columns"
+          columnMode="flex"
+          [toolHeader]="false"
+          [header]="false"
+          [footer]="false"
+          [limit]="0">
+</cd-table>
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.scss b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts
new file mode 100644 (file)
index 0000000..a116866
--- /dev/null
@@ -0,0 +1,30 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+
+import { NgxDatatableModule } from '@swimlane/ngx-datatable';
+
+import { TableComponent } from '../table/table.component';
+import { TableKeyValueComponent } from './table-key-value.component';
+
+describe('TableKeyValueComponent', () => {
+  let component: TableKeyValueComponent;
+  let fixture: ComponentFixture<TableKeyValueComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TableComponent, TableKeyValueComponent ],
+      imports: [ FormsModule, NgxDatatableModule ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TableKeyValueComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.ts
new file mode 100644 (file)
index 0000000..b8af65a
--- /dev/null
@@ -0,0 +1,57 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+import { CellTemplate } from '../../enum/cell-template.enum';
+import { CdTableColumn } from '../../models/cd-table-column';
+
+/**
+ * Display the given data in a 2 column data table. The left column
+ * shows the 'key' attribute, the right column the 'value' attribute.
+ * The data table has the following characteristics:
+ * - No header and footer is displayed
+ * - The relation of the width for the columns 'key' and 'value' is 1:3
+ * - The 'key' column is displayed in bold text
+ */
+@Component({
+  selector: 'cd-table-key-value',
+  templateUrl: './table-key-value.component.html',
+  styleUrls: ['./table-key-value.component.scss']
+})
+export class TableKeyValueComponent implements OnInit {
+
+  columns: Array<CdTableColumn> = [];
+
+  /**
+   * An array of objects to be displayed in the data table.
+   */
+  @Input() data: Array<object> = [];
+
+  /**
+   * The name of the attribute to be displayed as key.
+   * Defaults to 'key'.
+   * @type {string}
+   */
+  @Input() key = 'key';
+
+  /**
+   * The name of the attribute to be displayed as value.
+   * Defaults to 'value'.
+   * @type {string}
+   */
+  @Input() value = 'value';
+
+  constructor() { }
+
+  ngOnInit() {
+    this.columns = [
+      {
+        prop: this.key,
+        flexGrow: 1,
+        cellTransformation: CellTemplate.bold
+      },
+      {
+        prop: this.value,
+        flexGrow: 3
+      }
+    ];
+  }
+}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.html b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.html
new file mode 100644 (file)
index 0000000..f51aa6f
--- /dev/null
@@ -0,0 +1,76 @@
+<div class="dataTables_wrapper">
+  <div class="dataTables_header clearfix"
+       *ngIf="toolHeader">
+    <!-- actions -->
+    <div class="oadatatableactions">
+        <ng-content select="table-actions"></ng-content>
+    </div>
+    <!-- end actions -->
+
+    <!-- search -->
+    <div class="input-group">
+      <span class="input-group-addon">
+        <i class="glyphicon glyphicon-search"></i>
+      </span>
+      <input class="form-control"
+             type="text"
+             [(ngModel)]="search"
+             (keyup)='updateFilter($event)'>
+      <span class="input-group-btn">
+        <button type="button"
+                class="btn btn-default clear-input tc_clearInputBtn"
+                (click)="updateFilter()">
+          <i class="icon-prepend fa fa-remove"></i>
+        </button>
+      </span>
+    </div>
+    <!-- end search -->
+
+    <!-- pagination limit -->
+    <div class="input-group dataTables_paginate">
+      <input class="form-control"
+             type="number"
+             min="1"
+             max="9999"
+             [value]="limit"
+             (click)="setLimit($event)"
+             (keyup)="setLimit($event)"
+             (blur)="setLimit($event)">
+    </div>
+    <!-- end pagination limit-->
+
+    <!-- refresh button -->
+    <div class="widget-toolbar tc_refreshBtn">
+      <a (click)="reloadData()">
+        <i class="fa fa-lg fa-refresh"></i>
+      </a>
+    </div>
+    <!-- end refresh button -->
+  </div>
+  <ngx-datatable #table
+                 class="bootstrap oadatatable"
+                 [cssClasses]="paginationClasses"
+                 [selectionType]="selectionType"
+                 [selected]="selected"
+                 (select)="toggleExpandRow()"
+                 [columns]="columns"
+                 [columnMode]="columnMode"
+                 [rows]="rows"
+                 [rowClass]="getRowClass()"
+                 [headerHeight]="header ? 'auto' : 0"
+                 [footerHeight]="footer ? 'auto' : 0"
+                 [limit]="limit > 0 ? limit : undefined"
+                 [loadingIndicator]="true"
+                 [rowHeight]="'auto'">
+    <!-- Row Detail Template -->
+    <ngx-datatable-row-detail (toggle)="updateDetailView()">
+    </ngx-datatable-row-detail>
+  </ngx-datatable>
+</div>
+<ng-template cdTableDetails></ng-template>
+<!-- cell templates that can be accessed from outside -->
+<ng-template #tableCellBoldTpl
+             let-row="row"
+             let-value="value">
+  <strong>{{ value }}</strong>
+</ng-template>
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.scss b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.scss
new file mode 100644 (file)
index 0000000..2d19806
--- /dev/null
@@ -0,0 +1,182 @@
+@import '../../../../defaults';
+
+.dataTables_wrapper {
+  margin-bottom: 25px;
+  .separator {
+    height: 30px;
+    border-left: 1px solid rgba(0,0,0,.09);
+    padding-left: 5px;
+    margin-left: 5px;
+    display: inline-block;
+    vertical-align: middle;
+  }
+  .widget-toolbar {
+    display: inline-block;
+    float: right;
+    width: auto;
+    height: 30px;
+    line-height: 28px;
+    position: relative;
+    border-left: 1px solid rgba(0,0,0,.09);
+    cursor: pointer;
+    padding: 0 8px;
+    text-align: center;
+  }
+  .dropdown-menu {
+    white-space: nowrap;
+    & > li {
+      cursor: pointer;
+      & > label {
+        width: 100%;
+        margin-bottom: 0;
+        padding-left: 20px;
+        padding-right: 20px;
+        cursor: pointer;
+        &:hover {
+          background-color: #f5f5f5;
+        }
+        & > input {
+          cursor: pointer;
+        }
+      }
+    }
+  }
+  th.oadatatablecheckbox {
+    width: 16px;
+  }
+  .dataTables_length>input {
+    line-height: 25px;
+    text-align: right;
+  }
+}
+.dataTables_header {
+  background-color: #f6f6f6;
+  border: 1px solid #d1d1d1;
+  border-bottom: none;
+  padding: 5px;
+  position: relative;
+  .oadatatableactions {
+    display: inline-block;
+  }
+  .input-group {
+    float: right;
+    border-left: 1px solid rgba(0,0,0,.09);
+    padding-left: 8px;
+    width: 40%;
+    max-width: 350px;
+    .form-control {
+      height: 30px;
+    }
+    .clear-input {
+      height: 30px;
+      i {
+        vertical-align: text-top;
+      }
+    }
+  }
+  .input-group.dataTables_paginate {
+    width: 8%;
+  }
+}
+
+::ng-deep .oadatatable {
+  border: $border-color;
+  margin-bottom: 0;
+  max-width: none!important;
+  .datatable-header {
+    background-clip: padding-box;
+    background-color: #f9f9f9;
+    background-image: -webkit-linear-gradient(top,#fafafa 0,#ededed 100%);
+    background-image: -o-linear-gradient(top,#fafafa 0,#ededed 100%);
+    background-image: linear-gradient(to bottom,#fafafa 0,#ededed 100%);
+    background-repeat: repeat-x;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffafafa', endColorstr='#ffededed', GradientType=0);
+    .sort-asc, .sort-desc {
+      color: $oa-color-blue;
+    }
+    .datatable-header-cell{
+      @include table-cell;
+      text-align: left;
+      font-weight: bold;
+      .datatable-header-cell-label {
+        &:after {
+          font-family: FontAwesome;
+          font-weight: 400;
+          height: 9px;
+          left: 10px;
+          line-height: 12px;
+          position: relative;
+          vertical-align: baseline;
+          width: 12px;
+        }
+      }
+      &.sortable {
+        .datatable-header-cell-label:after {
+          content: " \f0dc";
+        }
+        &.sort-active {
+          &.sort-asc .datatable-header-cell-label:after {
+            content: " \f160";
+          }
+          &.sort-desc .datatable-header-cell-label:after {
+            content: " \f161";
+          }
+        }
+      }
+      &:first-child {
+        border-left: none;
+      }
+    }
+  }
+  .datatable-body {
+    .datatable-body-row {
+      &.clickable:hover .datatable-row-group {
+        background-color: #eee;
+        transition-property: background;
+        transition-duration: .3s;
+        transition-timing-function: linear;
+      }
+      &.datatable-row-even {
+        background-color: #ffffff;
+      }
+      &.datatable-row-odd {
+        background-color: #f6f6f6;
+      }
+      &.active, &.active:hover {
+        background-color: $bg-color-light-blue;
+      }
+      .datatable-body-cell{
+        @include table-cell;
+        &:first-child {
+          border-left: none;
+        }
+        .datatable-body-cell-label {
+          display: block;
+        }
+      }
+    }
+  }
+  .datatable-footer {
+    .selected-count, .page-count {
+      font-style: italic;
+      padding-left: 5px;
+    }
+    .datatable-pager .pager {
+      margin-right: 5px;
+      .pages {
+        & > a, & > span {
+          display: inline-block;
+          padding: 5px 10px;
+          margin-bottom: 5px;
+          border: none;
+        }
+        a:hover {
+          background-color: $oa-color-light-blue;
+        }
+        &.active > a {
+          background-color: $bg-color-light-blue;
+        }
+      }
+    }
+  }
+}
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.spec.ts
new file mode 100644 (file)
index 0000000..07a1825
--- /dev/null
@@ -0,0 +1,88 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+
+import { NgxDatatableModule, TableColumn } from '@swimlane/ngx-datatable';
+
+import { TableComponent } from './table.component';
+
+describe('TableComponent', () => {
+  let component: TableComponent;
+  let fixture: ComponentFixture<TableComponent>;
+  const columns: TableColumn[] = [];
+  const createFakeData = (n) => {
+    const data = [];
+    for (let i = 0; i < n; i++) {
+      data.push({
+        a: i,
+        b: i * i,
+        c: -(i % 10)
+      });
+    }
+    return data;
+  };
+
+  beforeEach(
+    async(() => {
+      TestBed.configureTestingModule({
+        declarations: [TableComponent],
+        imports: [NgxDatatableModule, FormsModule]
+      }).compileComponents();
+    })
+  );
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TableComponent);
+    component = fixture.componentInstance;
+  });
+
+  beforeEach(() => {
+    component.data = createFakeData(100);
+    component.useData();
+    component.columns = [
+      {prop: 'a'},
+      {prop: 'b'},
+      {prop: 'c'}
+    ];
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should have rows', () => {
+    expect(component.data.length).toBe(100);
+    expect(component.rows.length).toBe(component.data.length);
+  });
+
+  it('should have an int in setLimit parsing a string', () => {
+    expect(component.limit).toBe(10);
+    expect(component.limit).toEqual(jasmine.any(Number));
+
+    const e = {target: {value: '1'}};
+    component.setLimit(e);
+    expect(component.limit).toBe(1);
+    expect(component.limit).toEqual(jasmine.any(Number));
+    e.target.value = '-20';
+    component.setLimit(e);
+    expect(component.limit).toBe(1);
+  });
+
+  it('should search for 13', () => {
+    component.search = '13';
+    expect(component.rows.length).toBe(100);
+    component.updateFilter(true);
+    expect(component.rows[0].a).toBe(13);
+    expect(component.rows[1].b).toBe(1369);
+    expect(component.rows[2].b).toBe(3136);
+    expect(component.rows.length).toBe(3);
+  });
+
+  it('should restore full table after search', () => {
+    component.search = '13';
+    expect(component.rows.length).toBe(100);
+    component.updateFilter(true);
+    expect(component.rows.length).toBe(3);
+    component.updateFilter();
+    expect(component.rows.length).toBe(100);
+  });
+});
diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/shared/datatable/table/table.component.ts
new file mode 100644 (file)
index 0000000..ddbe24f
--- /dev/null
@@ -0,0 +1,175 @@
+import {
+  AfterContentChecked,
+  Component,
+  ComponentFactoryResolver,
+  EventEmitter,
+  Input,
+  OnChanges,
+  OnInit,
+  Output,
+  TemplateRef,
+  Type,
+  ViewChild
+} from '@angular/core';
+
+import { DatatableComponent } from '@swimlane/ngx-datatable';
+import * as _ from 'lodash';
+
+import { CdTableColumn } from '../../models/cd-table-column';
+import { TableDetailsDirective } from '../table-details.directive';
+
+@Component({
+  selector: 'cd-table',
+  templateUrl: './table.component.html',
+  styleUrls: ['./table.component.scss']
+})
+export class TableComponent implements AfterContentChecked, OnInit, OnChanges {
+  @ViewChild(DatatableComponent) table: DatatableComponent;
+  @ViewChild(TableDetailsDirective) detailTemplate: TableDetailsDirective;
+  @ViewChild('tableCellBoldTpl') tableCellBoldTpl: TemplateRef<any>;
+
+  // This is the array with the items to be shown.
+  @Input() data: any[];
+  // Each item -> { prop: 'attribute name', name: 'display name' }
+  @Input() columns: CdTableColumn[];
+  // Method used for setting column widths.
+  @Input() columnMode ?= 'force';
+  // Name of the component e.g. 'TableDetailsComponent'
+  @Input() detailsComponent?: string;
+  // Display the tool header, including reload button, pagination and search fields?
+  @Input() toolHeader ?= true;
+  // Display the table header?
+  @Input() header ?= true;
+  // Display the table footer?
+  @Input() footer ?= true;
+  // Page size to show. Set to 0 to show unlimited number of rows.
+  @Input() limit ?= 10;
+  // An optional function that is called before the details page is show.
+  // The current selection is passed as function argument. To do not display
+  // the details page, return false.
+  @Input() beforeShowDetails: Function;
+  // Should be the function that will update the input data.
+  @Output() fetchData = new EventEmitter();
+
+  cellTemplates: {
+    [key: string]: TemplateRef<any>
+  } = {};
+  selectionType: string = undefined;
+  search = '';
+  rows = [];
+  selected = [];
+  paginationClasses = {
+    pagerLeftArrow: 'i fa fa-angle-double-left',
+    pagerRightArrow: 'i fa fa-angle-double-right',
+    pagerPrevious: 'i fa fa-angle-left',
+    pagerNext: 'i fa fa-angle-right'
+  };
+
+  // Internal variable to check if it is necessary to recalculate the
+  // table columns after the browser window has been resized.
+  private currentWidth: number;
+
+  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
+
+  ngOnInit() {
+    this._addTemplates();
+    this.columns.map((column) => {
+      if (column.cellTransformation) {
+        column.cellTemplate = this.cellTemplates[column.cellTransformation];
+      }
+      return column;
+    });
+    this.reloadData();
+    if (this.detailsComponent) {
+      this.selectionType = 'multi';
+    }
+  }
+
+  ngAfterContentChecked() {
+    // If the data table is not visible, e.g. another tab is active, and the
+    // browser window gets resized, the table and its columns won't get resized
+    // automatically if the tab gets visible again.
+    // https://github.com/swimlane/ngx-datatable/issues/193
+    // https://github.com/swimlane/ngx-datatable/issues/193#issuecomment-329144543
+    if (this.table && this.table.element.clientWidth !== this.currentWidth) {
+      this.currentWidth = this.table.element.clientWidth;
+      // Force the redrawing of the table.
+      window.dispatchEvent(new Event('resize'));
+    }
+  }
+
+  _addTemplates () {
+    this.cellTemplates.bold = this.tableCellBoldTpl;
+  }
+
+  ngOnChanges(changes) {
+    this.useData();
+  }
+
+  setLimit(e) {
+    const value = parseInt(e.target.value, 10);
+    if (value > 0) {
+      this.limit = value;
+    }
+  }
+
+  reloadData() {
+    this.fetchData.emit();
+  }
+
+  useData() {
+    this.rows = [...this.data];
+  }
+
+  toggleExpandRow() {
+    if (this.selected.length > 0) {
+      this.table.rowDetail.toggleExpandRow(this.selected[0]);
+    } else {
+      this.detailTemplate.viewContainerRef.clear();
+    }
+  }
+
+  updateDetailView() {
+    if (!this.detailsComponent) {
+      return;
+    }
+    if (_.isFunction(this.beforeShowDetails)) {
+      if (!this.beforeShowDetails(this.selected)) {
+        return;
+      }
+    }
+    const factories = Array.from(this.componentFactoryResolver['_factories'].keys());
+    const factoryClass = <Type<any>>factories.find((x: any) => x.name === this.detailsComponent);
+    this.detailTemplate.viewContainerRef.clear();
+    const cmpRef = this.detailTemplate.viewContainerRef.createComponent(
+      this.componentFactoryResolver.resolveComponentFactory(factoryClass)
+    );
+    cmpRef.instance.selected = this.selected;
+  }
+
+  updateFilter(event?) {
+    if (!event) {
+      this.search = '';
+    }
+    const val = this.search.toLowerCase();
+    const columns = this.columns;
+    // update the rows
+    this.rows = this.data.filter(function (d) {
+      return columns.filter((c) => {
+        return (typeof d[c.prop] === 'string' || typeof d[c.prop] === 'number')
+          && (d[c.prop] + '').toLowerCase().indexOf(val) !== -1;
+      }).length > 0;
+    });
+    // Whenever the filter changes, always go back to the first page
+    this.table.offset = 0;
+  }
+
+  getRowClass() {
+    // Return the function used to populate a row's CSS classes.
+    return () => {
+      return {
+        'clickable': !_.isUndefined(this.detailsComponent)
+      };
+    };
+  }
+}
index 09aa9e7223220aeff216265a354a59340beb7c16..678c1b40524264f487d239804daf1f50cdcfd347 100644 (file)
@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 
 import { ComponentsModule } from './components/components.module';
+import { DataTableModule } from './datatable/datatable.module';
 import { PasswordButtonDirective } from './directives/password-button.directive';
 import { PipesModule } from './pipes/pipes.module';
 import { AuthGuardService } from './services/auth-guard.service';
@@ -17,14 +18,16 @@ import { ServicesModule } from './services/services.module';
     CommonModule,
     PipesModule,
     ComponentsModule,
-    ServicesModule
+    ServicesModule,
+    DataTableModule
   ],
   exports: [
     PipesModule,
     ComponentsModule,
     ServicesModule,
     PasswordButtonDirective,
-    ComponentsModule
+    ComponentsModule,
+    DataTableModule
   ],
   declarations: [
     PasswordButtonDirective