Просмотр исходного кода

feat(panels): upgraded table panel to latest plugin model

Torkel Ödegaard 10 лет назад
Родитель
Сommit
9bd3b417c4

+ 10 - 7
public/app/features/panel/panel_ctrl.ts

@@ -80,13 +80,16 @@ export class PanelCtrl {
     return;
     return;
   }
   }
 
 
-  addEditorTab(title, templateUrl) {
-    this.editorTabs.push({
-      title: title,
-      directiveFn: function() {
-        return {templateUrl: templateUrl};
-      }
-    });
+  addEditorTab(title, directiveFn) {
+    var editorTab = {title, directiveFn};
+
+    if (_.isString(directiveFn)) {
+      editorTab.directiveFn = function() {
+        return {templateUrl: directiveFn};
+      };
+    }
+
+    this.editorTabs.push(editorTab);
   }
   }
 
 
   getMenu() {
   getMenu() {

+ 108 - 109
public/app/plugins/panel/table/controller.ts

@@ -4,132 +4,131 @@ import angular from 'angular';
 import _ from 'lodash';
 import _ from 'lodash';
 import moment from 'moment';
 import moment from 'moment';
 import * as FileExport from 'app/core/utils/file_export';
 import * as FileExport from 'app/core/utils/file_export';
-import PanelMeta from 'app/features/panel/panel_meta2';
+import {MetricsPanelCtrl} from '../../../features/panel/panel';
 import {transformDataToTable} from './transformers';
 import {transformDataToTable} from './transformers';
-
-export class TablePanelCtrl {
+import {tablePanelEditor} from './editor';
+
+var panelDefaults = {
+  targets: [{}],
+  transform: 'timeseries_to_columns',
+  pageSize: null,
+  showHeader: true,
+  styles: [
+    {
+      type: 'date',
+      pattern: 'Time',
+      dateFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    {
+      unit: 'short',
+      type: 'number',
+      decimals: 2,
+      colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
+      colorMode: null,
+      pattern: '/.*/',
+      thresholds: [],
+    }
+  ],
+  columns: [],
+  scroll: true,
+  fontSize: '100%',
+  sort: {col: 0, desc: true},
+};
+
+export class TablePanelCtrl extends MetricsPanelCtrl {
+  pageIndex: number;
+  dataRaw: any;
+  table: any;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor($scope, $rootScope, $q, panelSrv, panelHelper, annotationsSrv) {
-    $scope.ctrl = this;
-    $scope.pageIndex = 0;
-
-    $scope.panelMeta = new PanelMeta({
-      panelName: 'Table',
-      editIcon:  "fa fa-table",
-      fullscreen: true,
-      metricsEditor: true,
-    });
-
-    $scope.panelMeta.addEditorTab('Options', 'app/plugins/panel/table/options.html');
-    $scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
-    $scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()');
-
-    var panelDefaults = {
-      targets: [{}],
-      transform: 'timeseries_to_columns',
-      pageSize: null,
-      showHeader: true,
-      styles: [
-        {
-          type: 'date',
-          pattern: 'Time',
-          dateFormat: 'YYYY-MM-DD HH:mm:ss',
-        },
-        {
-          unit: 'short',
-          type: 'number',
-          decimals: 2,
-          colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
-          colorMode: null,
-          pattern: '/.*/',
-          thresholds: [],
-        }
-      ],
-      columns: [],
-      scroll: true,
-      fontSize: '100%',
-      sort: {col: 0, desc: true},
-    };
-
-    $scope.init = function() {
-      if ($scope.panel.styles === void 0) {
-        $scope.panel.styles = $scope.panel.columns;
-        $scope.panel.columns = $scope.panel.fields;
-        delete $scope.panel.columns;
-        delete $scope.panel.fields;
-      }
-
-      _.defaults($scope.panel, panelDefaults);
-      panelSrv.init($scope);
-    };
+  constructor($scope, $injector, private annotationsSrv) {
+    super($scope, $injector);
+    this.pageIndex = 0;
+
+    if (this.panel.styles === void 0) {
+      this.panel.styles = this.panel.columns;
+      this.panel.columns = this.panel.fields;
+      delete this.panel.columns;
+      delete this.panel.fields;
+    }
+
+    _.defaults(this.panel, panelDefaults);
+  }
 
 
-    $scope.refreshData = function(datasource) {
-      panelHelper.updateTimeRange($scope);
+  initEditMode() {
+    super.initEditMode();
+    this.addEditorTab('Options', tablePanelEditor);
+    this.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
+  }
 
 
-      $scope.pageIndex = 0;
+  getExtendedMenu() {
+    var menu = super.getExtendedMenu();
+    menu.push({text: 'Export CSV', click: 'exportCsv()'});
+    return menu;
+  }
 
 
-      if ($scope.panel.transform === 'annotations') {
-        return annotationsSrv.getAnnotations($scope.dashboard).then(annotations => {
-          $scope.dataRaw = annotations;
-          $scope.render();
-        });
-      }
+  refreshData(datasource) {
+    this.pageIndex = 0;
 
 
-      return panelHelper.issueMetricQuery($scope, datasource)
-      .then($scope.dataHandler, function(err) {
-        $scope.render();
-        throw err;
+    if (this.panel.transform === 'annotations') {
+      return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
+        this.dataRaw = annotations;
+        this.render();
       });
       });
-    };
+    }
 
 
-    $scope.toggleColumnSort = function(col, colIndex) {
-      if ($scope.panel.sort.col === colIndex) {
-        if ($scope.panel.sort.desc) {
-          $scope.panel.sort.desc = false;
-        } else {
-          $scope.panel.sort.col = null;
-        }
+    return this.issueQueries(datasource)
+    .then(this.dataHandler.bind(this))
+    .catch(err => {
+      this.render();
+      throw err;
+    });
+  }
+
+  toggleColumnSort(col, colIndex) {
+    if (this.panel.sort.col === colIndex) {
+      if (this.panel.sort.desc) {
+        this.panel.sort.desc = false;
       } else {
       } else {
-        $scope.panel.sort.col = colIndex;
-        $scope.panel.sort.desc = true;
+        this.panel.sort.col = null;
       }
       }
+    } else {
+      this.panel.sort.col = colIndex;
+      this.panel.sort.desc = true;
+    }
+
+    this.render();
+  }
 
 
-      $scope.render();
-    };
-
-    $scope.dataHandler = function(results) {
-      $scope.dataRaw = results.data;
-      $scope.pageIndex = 0;
-      $scope.render();
-    };
-
-    $scope.render = function() {
-      // automatically correct transform mode
-      // based on data
-      if ($scope.dataRaw && $scope.dataRaw.length) {
-        if ($scope.dataRaw[0].type === 'table') {
-          $scope.panel.transform = 'table';
+  dataHandler(results) {
+    this.dataRaw = results.data;
+    this.pageIndex = 0;
+    this.render();
+  }
+
+  render() {
+    // automatically correct transform mode
+    // based on data
+    if (this.dataRaw && this.dataRaw.length) {
+      if (this.dataRaw[0].type === 'table') {
+        this.panel.transform = 'table';
+      } else {
+        if (this.dataRaw[0].type === 'docs') {
+          this.panel.transform = 'json';
         } else {
         } else {
-          if ($scope.dataRaw[0].type === 'docs') {
-            $scope.panel.transform = 'json';
-          } else {
-            if ($scope.panel.transform === 'table' || $scope.panel.transform === 'json') {
-              $scope.panel.transform = 'timeseries_to_rows';
-            }
+          if (this.panel.transform === 'table' || this.panel.transform === 'json') {
+            this.panel.transform = 'timeseries_to_rows';
           }
           }
         }
         }
       }
       }
+    }
 
 
-      $scope.table = transformDataToTable($scope.dataRaw, $scope.panel);
-      $scope.table.sort($scope.panel.sort);
-      panelHelper.broadcastRender($scope, $scope.table, $scope.dataRaw);
-    };
-
-    $scope.exportCsv = function() {
-      FileExport.exportTableDataToCsv($scope.table);
-    };
+    this.table = transformDataToTable(this.dataRaw, this.panel);
+    this.table.sort(this.panel.sort);
+    this.broadcastRender(this.table);
+  }
 
 
-    $scope.init();
+  exportCsv() {
+    FileExport.exportTableDataToCsv(this.table);
   }
   }
 }
 }

+ 25 - 25
public/app/plugins/panel/table/editor.html

@@ -9,9 +9,9 @@
 					</li>
 					</li>
 					<li>
 					<li>
 						<select class="input-large tight-form-input"
 						<select class="input-large tight-form-input"
-							ng-model="panel.transform"
-							ng-options="k as v.description for (k, v) in transformers"
-							ng-change="transformChanged()"></select>
+							ng-model="editor.panel.transform"
+							ng-options="k as v.description for (k, v) in editor.transformers"
+							ng-change="editor.transformChanged()"></select>
 					</li>
 					</li>
 				</ul>
 				</ul>
 				<div class="clearfix"></div>
 				<div class="clearfix"></div>
@@ -21,14 +21,14 @@
 					<li class="tight-form-item" style="width: 140px">
 					<li class="tight-form-item" style="width: 140px">
 						Columns
 						Columns
 					</li>
 					</li>
-					<li class="tight-form-item" ng-repeat="column in panel.columns">
-						<i class="pointer fa fa-remove" ng-click="removeColumn(column)"></i>
+					<li class="tight-form-item" ng-repeat="column in editor.panel.columns">
+						<i class="pointer fa fa-remove" ng-click="editor.removeColumn(column)"></i>
 						<span>
 						<span>
 							{{column.text}}
 							{{column.text}}
 						</span>
 						</span>
 					</li>
 					</li>
 					<li>
 					<li>
-						<metric-segment segment="addColumnSegment" get-options="getColumnOptions()" on-change="addColumn()"></metric-segment>
+						<metric-segment segment="editor.addColumnSegment" get-options="editor.getColumnOptions()" on-change="editor.addColumn()"></metric-segment>
 					</li>
 					</li>
 				</ul>
 				</ul>
 				<div class="clearfix"></div>
 				<div class="clearfix"></div>
@@ -46,16 +46,16 @@
 					</li>
 					</li>
 					<li>
 					<li>
 						<input type="number" class="input-small tight-form-input" placeholder="100"
 						<input type="number" class="input-small tight-form-input" placeholder="100"
-						empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
+						empty-to-null ng-model="editor.panel.pageSize" ng-change="editor.render()" ng-model-onblur>
 					</li>
 					</li>
 					<li class="tight-form-item">
 					<li class="tight-form-item">
-						<editor-checkbox text="Scroll" model="panel.scroll" change="render()"></editor-checkbox>
+						<editor-checkbox text="Scroll" model="editor.panel.scroll" change="editor.render()"></editor-checkbox>
 					</li>
 					</li>
 					<li class="tight-form-item">
 					<li class="tight-form-item">
 						Font size
 						Font size
 					</li>
 					</li>
 					<li>
 					<li>
-						<select class="input-small tight-form-input" ng-model="panel.fontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
+						<select class="input-small tight-form-input" ng-model="editor.panel.fontSize" ng-options="f for f in editor.fontSizes" ng-change="editor.render()"></select>
 					</li>
 					</li>
 				</ul>
 				</ul>
 				<div class="clearfix"></div>
 				<div class="clearfix"></div>
@@ -68,11 +68,11 @@
 	<h5>Column Styles</h5>
 	<h5>Column Styles</h5>
 
 
 	<div class="tight-form-container">
 	<div class="tight-form-container">
-		<div ng-repeat="style in panel.styles">
+		<div ng-repeat="style in editor.panel.styles">
 			<div class="tight-form">
 			<div class="tight-form">
 				<ul class="tight-form-list pull-right">
 				<ul class="tight-form-list pull-right">
 					<li class="tight-form-item last">
 					<li class="tight-form-item last">
-						<i class="fa fa-remove pointer" ng-click="removeColumnStyle(style)"></i>
+						<i class="fa fa-remove pointer" ng-click="editor.removeColumnStyle(style)"></i>
 					</li>
 					</li>
 				</ul>
 				</ul>
 
 
@@ -81,7 +81,7 @@
 						Name or regex
 						Name or regex
 					</li>
 					</li>
 					<li>
 					<li>
-						<input type="text" ng-model="style.pattern" bs-typeahead="getColumnNames" ng-blur="render()" data-min-length=0 data-items=100 class="input-medium tight-form-input">
+						<input type="text" ng-model="style.pattern" bs-typeahead="editor.getColumnNames" ng-blur="editor.render()" data-min-length=0 data-items=100 class="input-medium tight-form-input">
 					</li>
 					</li>
 					<li class="tight-form-item" style="width: 86px">
 					<li class="tight-form-item" style="width: 86px">
 						Type
 						Type
@@ -89,8 +89,8 @@
 					<li>
 					<li>
 						<select class="input-small tight-form-input"
 						<select class="input-small tight-form-input"
 							ng-model="style.type"
 							ng-model="style.type"
-							ng-options="c.value as c.text for c in columnTypes"
-							ng-change="render()"
+							ng-options="c.value as c.text for c in editor.columnTypes"
+							ng-change="editor.render()"
 							style="width: 150px"
 							style="width: 150px"
 							></select>
 							></select>
 					</li>
 					</li>
@@ -100,7 +100,7 @@
 						Format
 						Format
 					</li>
 					</li>
 					<li>
 					<li>
-						<metric-segment-model property="style.dateFormat" options="dateFormats" on-change="render()" custom="true"></metric-segment-model>
+						<metric-segment-model property="style.dateFormat" options="editor.dateFormats" on-change="editor.render()" custom="true"></metric-segment-model>
 					</li>
 					</li>
 				</ul>
 				</ul>
 				<div class="clearfix"></div>
 				<div class="clearfix"></div>
@@ -113,8 +113,8 @@
 					<li>
 					<li>
 						<select class="input-small tight-form-input"
 						<select class="input-small tight-form-input"
 							ng-model="style.colorMode"
 							ng-model="style.colorMode"
-							ng-options="c.value as c.text for c in colorModes"
-							ng-change="render()"
+							ng-options="c.value as c.text for c in editor.colorModes"
+							ng-change="editor.render()"
 							style="width: 150px"
 							style="width: 150px"
 							></select>
 							></select>
 					</li>
 					</li>
@@ -122,18 +122,18 @@
 						Thresholds<tip>Comma seperated values</tip>
 						Thresholds<tip>Comma seperated values</tip>
 					</li>
 					</li>
 					<li>
 					<li>
-						<input type="text" class="input-small tight-form-input" style="width: 150px" ng-model="style.thresholds" ng-blur="render()" placeholder="0,50,80" array-join></input>
+						<input type="text" class="input-small tight-form-input" style="width: 150px" ng-model="style.thresholds" ng-blur="editor.render()" placeholder="0,50,80" array-join></input>
 					</li>
 					</li>
 					<li class="tight-form-item" style="width: 60px">
 					<li class="tight-form-item" style="width: 60px">
 						Colors
 						Colors
 					</li>
 					</li>
 					<li class="tight-form-item">
 					<li class="tight-form-item">
-						<spectrum-picker ng-model="style.colors[0]" ng-change="render()" ></spectrum-picker>
-						<spectrum-picker ng-model="style.colors[1]" ng-change="render()" ></spectrum-picker>
-						<spectrum-picker ng-model="style.colors[2]" ng-change="render()" ></spectrum-picker>
+						<spectrum-picker ng-model="style.colors[0]" ng-change="editor.render()" ></spectrum-picker>
+						<spectrum-picker ng-model="style.colors[1]" ng-change="editor.render()" ></spectrum-picker>
+						<spectrum-picker ng-model="style.colors[2]" ng-change="editor.render()" ></spectrum-picker>
 					</li>
 					</li>
 					<li class="tight-form-item last">
 					<li class="tight-form-item last">
-						<a class="pointer" ng-click="invertColorOrder($index)">invert order</a>
+						<a class="pointer" ng-click="editor.invertColorOrder($index)">invert order</a>
 					</li>
 					</li>
 				</ul>
 				</ul>
 				<div class="clearfix"></div>
 				<div class="clearfix"></div>
@@ -145,8 +145,8 @@
 					</li>
 					</li>
 					<li class="dropdown" style="width: 150px"
 					<li class="dropdown" style="width: 150px"
 						ng-model="style.unit"
 						ng-model="style.unit"
-						dropdown-typeahead="unitFormats"
-						dropdown-typeahead-on-select="setUnitFormat(style, $subItem)">
+						dropdown-typeahead="editor.unitFormats"
+						dropdown-typeahead-on-select="editor.setUnitFormat(style, $subItem)">
 					</li>
 					</li>
 					<li class="tight-form-item" style="width: 86px">
 					<li class="tight-form-item" style="width: 86px">
 						Decimals
 						Decimals
@@ -161,7 +161,7 @@
 		</div>
 		</div>
 	</div>
 	</div>
 
 
-	<button class="btn btn-inverse" style="margin-top: 20px" ng-click="addColumnStyle()">
+	<button class="btn btn-inverse" style="margin-top: 20px" ng-click="editor.addColumnStyle()">
 		Add column style rule
 		Add column style rule
 	</button>
 	</button>
 </div>
 </div>

+ 86 - 68
public/app/plugins/panel/table/editor.ts

@@ -10,105 +10,123 @@ import {transformers} from './transformers';
 import kbn from 'app/core/utils/kbn';
 import kbn from 'app/core/utils/kbn';
 
 
 export class TablePanelEditorCtrl {
 export class TablePanelEditorCtrl {
+  panel: any;
+  panelCtrl: any;
+  transformers: any;
+  colorModes: any;
+  columnStyles: any;
+  columnTypes: any;
+  fontSizes: any;
+  dateFormats: any;
+  addColumnSegment: any;
+  unitFormats: any;
+  getColumnNames: any;
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor($scope, $q, uiSegmentSrv) {
-    $scope.transformers = transformers;
-    $scope.unitFormats = kbn.getUnitFormats();
-    $scope.colorModes = [
+  constructor($scope, private $q, private uiSegmentSrv) {
+    $scope.editor = this;
+    this.panelCtrl = $scope.ctrl;
+    this.panel = this.panelCtrl.panel;
+    this.transformers = transformers;
+    this.unitFormats = kbn.getUnitFormats();
+    this.colorModes = [
       {text: 'Disabled', value: null},
       {text: 'Disabled', value: null},
       {text: 'Cell', value: 'cell'},
       {text: 'Cell', value: 'cell'},
       {text: 'Value', value: 'value'},
       {text: 'Value', value: 'value'},
       {text: 'Row', value: 'row'},
       {text: 'Row', value: 'row'},
     ];
     ];
-    $scope.columnTypes = [
+    this.columnTypes = [
       {text: 'Number', value: 'number'},
       {text: 'Number', value: 'number'},
       {text: 'String', value: 'string'},
       {text: 'String', value: 'string'},
       {text: 'Date', value: 'date'},
       {text: 'Date', value: 'date'},
     ];
     ];
-    $scope.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
-    $scope.dateFormats = [
+    this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
+    this.dateFormats = [
       {text: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss'},
       {text: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss'},
       {text: 'MM/DD/YY h:mm:ss a', value: 'MM/DD/YY h:mm:ss a'},
       {text: 'MM/DD/YY h:mm:ss a', value: 'MM/DD/YY h:mm:ss a'},
       {text: 'MMMM D, YYYY LT',  value: 'MMMM D, YYYY LT'},
       {text: 'MMMM D, YYYY LT',  value: 'MMMM D, YYYY LT'},
     ];
     ];
 
 
-    $scope.addColumnSegment = uiSegmentSrv.newPlusButton();
+    this.addColumnSegment = uiSegmentSrv.newPlusButton();
 
 
-    $scope.getColumnOptions = function() {
-      if (!$scope.dataRaw) {
-        return $q.when([]);
+    // this is used from bs-typeahead and needs to be instance bound
+    this.getColumnNames = () => {
+      if (!this.panelCtrl.table) {
+        return [];
       }
       }
-      var columns = transformers[$scope.panel.transform].getColumns($scope.dataRaw);
-      var segments = _.map(columns, (c: any) => uiSegmentSrv.newSegment({value: c.text}));
-      return $q.when(segments);
+      return _.map(this.panelCtrl.table.columns, function(col: any) {
+        return col.text;
+      });
     };
     };
+  }
 
 
-    $scope.addColumn = function() {
-      var columns = transformers[$scope.panel.transform].getColumns($scope.dataRaw);
-      var column = _.findWhere(columns, {text: $scope.addColumnSegment.value});
+  getColumnOptions() {
+    if (!this.panelCtrl.dataRaw) {
+      return this.$q.when([]);
+    }
+    var columns = this.transformers[this.panel.transform].getColumns(this.panelCtrl.dataRaw);
+    var segments = _.map(columns, (c: any) => this.uiSegmentSrv.newSegment({value: c.text}));
+    return this.$q.when(segments);
+  }
 
 
-      if (column) {
-        $scope.panel.columns.push(column);
-        $scope.render();
-      }
+  addColumn() {
+    var columns = transformers[this.panel.transform].getColumns(this.panelCtrl.dataRaw);
+    var column = _.findWhere(columns, {text: this.addColumnSegment.value});
 
 
-      var plusButton = uiSegmentSrv.newPlusButton();
-      $scope.addColumnSegment.html = plusButton.html;
-      $scope.addColumnSegment.value = plusButton.value;
-    };
+    if (column) {
+      this.panel.columns.push(column);
+      this.render();
+    }
 
 
-    $scope.transformChanged = function() {
-      $scope.panel.columns = [];
-      $scope.render();
-    };
+    var plusButton = this.uiSegmentSrv.newPlusButton();
+    this.addColumnSegment.html = plusButton.html;
+    this.addColumnSegment.value = plusButton.value;
+  }
 
 
-    $scope.removeColumn = function(column) {
-      $scope.panel.columns = _.without($scope.panel.columns, column);
-      $scope.render();
-    };
+  transformChanged() {
+    this.panel.columns = [];
+    this.render();
+  }
 
 
-    $scope.setUnitFormat = function(column, subItem) {
-      column.unit = subItem.value;
-      $scope.render();
-    };
+  render() {
+    this.panelCtrl.render();
+  }
 
 
-    $scope.addColumnStyle = function() {
-      var columnStyleDefaults = {
-        unit: 'short',
-        type: 'number',
-        decimals: 2,
-        colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
-        colorMode: null,
-        pattern: '/.*/',
-        dateFormat: 'YYYY-MM-DD HH:mm:ss',
-        thresholds: [],
-      };
-
-      $scope.panel.styles.push(angular.copy(columnStyleDefaults));
-    };
+  removeColumn(column) {
+    this.panel.columns = _.without(this.panel.columns, column);
+    this.panelCtrl.render();
+  }
 
 
-    $scope.removeColumnStyle = function(style) {
-      $scope.panel.styles = _.without($scope.panel.styles, style);
-    };
+  setUnitFormat(column, subItem) {
+    column.unit = subItem.value;
+    this.panelCtrl.render();
+  };
 
 
-    $scope.getColumnNames = function() {
-      if (!$scope.table) {
-        return [];
-      }
-      return _.map($scope.table.columns, function(col: any) {
-        return col.text;
-      });
+  addColumnStyle() {
+    var columnStyleDefaults = {
+      unit: 'short',
+      type: 'number',
+      decimals: 2,
+      colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
+      colorMode: null,
+      pattern: '/.*/',
+      dateFormat: 'YYYY-MM-DD HH:mm:ss',
+      thresholds: [],
     };
     };
 
 
-    $scope.invertColorOrder = function(index) {
-      var ref = $scope.panel.styles[index].colors;
-      var copy = ref[0];
-      ref[0] = ref[2];
-      ref[2] = copy;
-      $scope.render();
-    };
+    this.panel.styles.push(angular.copy(columnStyleDefaults));
+  }
+
+  removeColumnStyle(style) {
+    this.panel.styles = _.without(this.panel.styles, style);
+  }
 
 
+  invertColorOrder(index) {
+    var ref = this.panel.styles[index].colors;
+    var copy = ref[0];
+    ref[0] = ref[2];
+    ref[2] = copy;
+    this.panelCtrl.render();
   }
   }
 }
 }
 
 

+ 23 - 27
public/app/plugins/panel/table/module.html

@@ -1,28 +1,24 @@
-<div class="table-panel-wrapper">
-	<grafana-panel>
-		<div class="table-panel-container">
-			<div class="table-panel-header-bg"></div>
-			<div class="table-panel-scroll">
-				<table class="table-panel-table">
-					<thead>
-						<tr>
-							<th ng-repeat="col in table.columns">
-								<div class="table-panel-table-header-inner pointer" ng-click="toggleColumnSort(col, $index)">
-									{{col.text}}
-									<span class="table-panel-table-header-controls" ng-if="col.sort">
-										<i class="fa fa-caret-down" ng-show="col.desc"></i>
-										<i class="fa fa-caret-up" ng-hide="col.desc"></i>
-									</span>
-								</div>
-							</th>
-						</tr>
-					</thead>
-					<tbody>
-					</tbody>
-				</table>
-			</div>
-		</div>
-		<div class="table-panel-footer">
-		</div>
-	</grafana-panel>
+<div class="table-panel-container">
+	<div class="table-panel-header-bg"></div>
+	<div class="table-panel-scroll">
+		<table class="table-panel-table">
+			<thead>
+				<tr>
+					<th ng-repeat="col in ctrl.table.columns">
+						<div class="table-panel-table-header-inner pointer" ng-click="ctrl.toggleColumnSort(col, $index)">
+							{{col.text}}
+							<span class="table-panel-table-header-controls" ng-if="col.sort">
+								<i class="fa fa-caret-down" ng-show="col.desc"></i>
+								<i class="fa fa-caret-up" ng-hide="col.desc"></i>
+							</span>
+						</div>
+					</th>
+				</tr>
+			</thead>
+			<tbody>
+			</tbody>
+		</table>
+	</div>
+</div>
+<div class="table-panel-footer">
 </div>
 </div>

+ 71 - 78
public/app/plugins/panel/table/module.ts

@@ -5,103 +5,96 @@ import kbn = require('app/core/utils/kbn');
 import _ from 'lodash';
 import _ from 'lodash';
 import $ from 'jquery';
 import $ from 'jquery';
 import moment from 'moment';
 import moment from 'moment';
-import angular from 'angular';
+import {PanelDirective} from '../../../features/panel/panel';
 import {TablePanelCtrl} from './controller';
 import {TablePanelCtrl} from './controller';
 import {TableRenderer} from './renderer';
 import {TableRenderer} from './renderer';
-import {tablePanelEditor} from './editor';
-
-angular.module('grafana.directives').directive('grafanaPanelTableEditor', tablePanelEditor);
-
-function tablePanel() {
-  'use strict';
-  return {
-    restrict: 'E',
-    templateUrl: 'app/plugins/panel/table/module.html',
-    controller: TablePanelCtrl,
-    link: function(scope, elem) {
-      var data;
-      var panel = scope.panel;
-      var pageCount = 0;
-      var formaters = [];
-
-      function getTableHeight() {
-        var panelHeight = scope.height || scope.panel.height || scope.row.height;
-        if (_.isString(panelHeight)) {
-          panelHeight = parseInt(panelHeight.replace('px', ''), 10);
-        }
-        if (pageCount > 1) {
-          panelHeight -= 28;
-        }
-
-        return (panelHeight - 60) + 'px';
-      }
 
 
-      function appendTableRows(tbodyElem) {
-        var renderer = new TableRenderer(panel, data, scope.dashboard.timezone);
-        tbodyElem.empty();
-        tbodyElem.html(renderer.render(scope.pageIndex));
-      }
+class TablePanel extends PanelDirective {
+  templateUrl = 'app/plugins/panel/table/module.html';
+  controller = TablePanelCtrl;
 
 
-      function switchPage(e) {
-        var el = $(e.currentTarget);
-        scope.pageIndex = (parseInt(el.text(), 10)-1);
-        renderPanel();
-      }
+  link(scope, elem, attrs, ctrl) {
+    var data;
+    var panel = ctrl.panel;
+    var pageCount = 0;
+    var formaters = [];
 
 
-      function appendPaginationControls(footerElem) {
-        footerElem.empty();
+    function getTableHeight() {
+      var panelHeight = ctrl.height || ctrl.panel.height || ctrl.row.height;
+      if (_.isString(panelHeight)) {
+        panelHeight = parseInt(panelHeight.replace('px', ''), 10);
+      }
+      if (pageCount > 1) {
+        panelHeight -= 28;
+      }
 
 
-        var pageSize = panel.pageSize || 100;
-        pageCount = Math.ceil(data.rows.length / pageSize);
-        if (pageCount === 1) {
-          return;
-        }
+      return (panelHeight - 60) + 'px';
+    }
 
 
-        var startPage = Math.max(scope.pageIndex - 3, 0);
-        var endPage = Math.min(pageCount, startPage + 9);
+    function appendTableRows(tbodyElem) {
+      var renderer = new TableRenderer(panel, data, ctrl.dashboard.timezone);
+      tbodyElem.empty();
+      tbodyElem.html(renderer.render(ctrl.pageIndex));
+    }
 
 
-        var paginationList = $('<ul></ul>');
+    function switchPage(e) {
+      var el = $(e.currentTarget);
+      ctrl.pageIndex = (parseInt(el.text(), 10)-1);
+      renderPanel();
+    }
 
 
-        for (var i = startPage; i < endPage; i++) {
-          var activeClass = i === scope.pageIndex ? 'active' : '';
-          var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
-          paginationList.append(pageLinkElem);
-        }
+    function appendPaginationControls(footerElem) {
+      footerElem.empty();
 
 
-        footerElem.append(paginationList);
+      var pageSize = panel.pageSize || 100;
+      pageCount = Math.ceil(data.rows.length / pageSize);
+      if (pageCount === 1) {
+        return;
       }
       }
 
 
-      function renderPanel() {
-        var container = elem.find('.table-panel-container');
-        var rootElem = elem.find('.table-panel-scroll');
-        var tbodyElem = elem.find('tbody');
-        var footerElem = elem.find('.table-panel-footer');
-
-        appendTableRows(tbodyElem);
+      var startPage = Math.max(ctrl.pageIndex - 3, 0);
+      var endPage = Math.min(pageCount, startPage + 9);
 
 
-        container.css({'font-size': panel.fontSize});
-        appendPaginationControls(footerElem);
+      var paginationList = $('<ul></ul>');
 
 
-        rootElem.css({'max-height': panel.scroll ? getTableHeight() : '' });
+      for (var i = startPage; i < endPage; i++) {
+        var activeClass = i === ctrl.pageIndex ? 'active' : '';
+        var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
+        paginationList.append(pageLinkElem);
       }
       }
 
 
-      elem.on('click', '.table-panel-page-link', switchPage);
+      footerElem.append(paginationList);
+    }
+
+    function renderPanel() {
+      var panelElem = elem.parents('.panel');
+      var rootElem = elem.find('.table-panel-scroll');
+      var tbodyElem = elem.find('tbody');
+      var footerElem = elem.find('.table-panel-footer');
 
 
-      scope.$on('$destroy', function() {
-        elem.off('click', '.table-panel-page-link');
-      });
+      elem.css({'font-size': panel.fontSize});
+      panelElem.addClass('table-panel-wrapper');
 
 
-      scope.$on('render', function(event, renderData) {
-        data = renderData || data;
-        if (!data) {
-          scope.get_data();
-          return;
-        }
+      appendTableRows(tbodyElem);
+      appendPaginationControls(footerElem);
 
 
-        renderPanel();
-      });
+      rootElem.css({'max-height': panel.scroll ? getTableHeight() : '' });
     }
     }
-  };
+
+    elem.on('click', '.table-panel-page-link', switchPage);
+
+    scope.$on('$destroy', function() {
+      elem.off('click', '.table-panel-page-link');
+    });
+
+    scope.$on('render', function(event, renderData) {
+      data = renderData || data;
+      renderPanel();
+    });
+  }
 }
 }
 
 
-export {tablePanel as panel};
+export {
+  TablePanel,
+  TablePanel as Panel
+};