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

feat(panel plugin): improving panel plugin model

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

+ 1 - 0
pkg/api/frontendsettings.go

@@ -123,6 +123,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
 		panels[panel.Id] = map[string]interface{}{
 			"module": panel.Module,
 			"name":   panel.Name,
+			"info":   panel.Info,
 		}
 	}
 

+ 6 - 3
public/app/core/filters/filters.ts

@@ -59,11 +59,14 @@ coreModule.filter('noXml', function() {
 
 coreModule.filter('interpolateTemplateVars', function (templateSrv) {
   var filterFunc: any = function(text, scope) {
-    if (scope.panel) {
-      return templateSrv.replaceWithText(text, scope.panel.scopedVars);
+    var scopedVars;
+    if (scope.ctrl && scope.ctrl.panel) {
+      scopedVars = scope.ctrl.panel.scopedVars;
     } else {
-      return templateSrv.replaceWithText(text, scope.row.scopedVars);
+      scopedVars = scope.row.scopedVars;
     }
+
+    return templateSrv.replaceWithText(text, scopedVars);
   };
 
   filterFunc.$stateful = true;

+ 1 - 0
public/app/features/panel/all.js

@@ -6,4 +6,5 @@ define([
   './solo_panel_ctrl',
   './panel_loader',
   './query_editor',
+  './panel_editor_tab',
 ], function () {});

+ 27 - 1
public/app/features/panel/panel_ctrl.ts → public/app/features/panel/panel.ts

@@ -1,15 +1,17 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import PanelMeta from './panel_meta2';
+import PanelMeta from './panel_meta3';
 
 export class PanelCtrl {
   meta: any;
   panel: any;
   row: any;
   dashboard: any;
+  tabIndex: number;
 
   constructor(private scope) {
     this.meta = new PanelMeta(this.panel);
+    this.tabIndex = 0;
     this.publishAppEvent('panel-instantiated', {scope: scope});
   }
 
@@ -36,4 +38,28 @@ export class PanelCtrl {
   }
 }
 
+export class PanelDirective {
+  template: string;
+  templateUrl: string;
+  bindToController: boolean;
+  scope: any;
+  controller: any;
+  controllerAs: string;
+
+  getDirective() {
+    return {
+      template: this.template,
+      templateUrl: this.templateUrl,
+      controller: this.controller,
+      controllerAs: 'ctrl',
+      bindToController: true,
+      scope: {dashboard: "=", panel: "=", row: "="},
+      link: this.link
+    };
+  }
+
+  link(scope) {
+    return null;
+  }
+}
 

+ 24 - 0
public/app/features/panel/panel_editor_tab.ts

@@ -0,0 +1,24 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import config from 'app/core/config';
+
+var directiveModule = angular.module('grafana.directives');
+
+/** @ngInject */
+function panelEditorTab(dynamicDirectiveSrv) {
+  return dynamicDirectiveSrv.create({
+    scope: {
+      panelCtrl: "=",
+      editorTab: "=",
+    },
+    directive: scope => {
+      return Promise.resolve({
+        name: 'panel-editor-tab-' + scope.editorTab.title,
+        fn: scope.editorTab.directiveFn,
+      });
+    }
+  });
+}
+
+directiveModule.directive('panelEditorTab', panelEditorTab);

+ 31 - 26
public/app/features/panel/panel_loader.ts

@@ -3,12 +3,12 @@
 import angular from 'angular';
 import config from 'app/core/config';
 
-import {unknownPanelDirective} from '../../plugins/panel/unknown/module';
+import {UnknownPanel} from '../../plugins/panel/unknown/module';
 
 var directiveModule = angular.module('grafana.directives');
 
 /** @ngInject */
-function panelLoader($compile, dynamicDirectiveSrv, $http, $q) {
+function panelLoader($compile, dynamicDirectiveSrv, $http, $q, $injector) {
   return {
     restrict: 'E',
     scope: {
@@ -18,11 +18,11 @@ function panelLoader($compile, dynamicDirectiveSrv, $http, $q) {
     },
     link: function(scope, elem, attrs) {
 
-      function getTemplate(component) {
-        if (component.template) {
-          return $q.when(component.template);
+      function getTemplate(directive) {
+        if (directive.template) {
+          return $q.when(directive.template);
         }
-        return $http.get(component.templateUrl).then(res => {
+        return $http.get(directive.templateUrl).then(res => {
           return res.data;
         });
       }
@@ -38,37 +38,42 @@ function panelLoader($compile, dynamicDirectiveSrv, $http, $q) {
         elem.append(child);
       }
 
-      function addPanel(name, directive) {
-        if (!directive.registered) {
-          getTemplate(directive).then(template => {
-            directive.templateUrl = null;
-            directive.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
-            directive.controllerAs =  'ctrl';
-            directive.bindToController =  true;
-            directive.scope = {
-              dashboard: "=",
-              panel: "=",
-              row: "="
-            };
+      function addPanel(name, Panel) {
+        if (Panel.registered) {
+          addPanelAndCompile(name);
+        }
 
-            directiveModule.directive(attrs.$normalize(name), function() {
-              return directive;
-            });
-            directive.registered = true;
+        if (Panel.promise) {
+          Panel.promise.then(() => {
             addPanelAndCompile(name);
           });
+          return;
         }
-        addPanelAndCompile(name);
+
+        var panelInstance = $injector.instantiate(Panel);
+        var directive = panelInstance.getDirective();
+
+        Panel.promise = getTemplate(directive).then(template => {
+          directive.templateUrl = null;
+          directive.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
+          directiveModule.directive(attrs.$normalize(name), function() {
+            return directive;
+          });
+          Panel.registered = true;
+          addPanelAndCompile(name);
+        });
+
+        return;
       }
 
       var panelElemName = 'panel-directive-' + scope.panel.type;
       let panelInfo = config.panels[scope.panel.type];
       if (!panelInfo) {
-        addPanel(panelElemName, unknownPanelDirective);
+        addPanel(panelElemName, UnknownPanel);
       }
 
       System.import(panelInfo.module).then(function(panelModule) {
-        addPanel(panelElemName, panelModule.panel);
+        addPanel(panelElemName, panelModule.Panel);
       }).catch(err => {
         console.log('Panel err: ', err);
       });
@@ -76,4 +81,4 @@ function panelLoader($compile, dynamicDirectiveSrv, $http, $q) {
   };
 }
 
-angular.module('grafana.directives').directive('panelLoader', panelLoader);
+directiveModule.directive('panelLoader', panelLoader);

+ 5 - 5
public/app/features/panel/panel_menu.js

@@ -11,9 +11,9 @@ function (angular, $, _) {
     .directive('panelMenu', function($compile, linkSrv) {
       var linkTemplate =
           '<span class="panel-title drag-handle pointer">' +
-            '<span class="panel-title-text drag-handle">{{ctrl.panel.title}}</span>' +
+            '<span class="panel-title-text drag-handle">{{ctrl.panel.title | interpolateTemplateVars:this}}</span>' +
             '<span class="panel-links-btn"><i class="fa fa-external-link"></i></span>' +
-            '<span class="panel-time-info" ng-show="ctrl.panelMeta.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.panelMeta.timeInfo}}</span>' +
+            '<span class="panel-time-info" ng-show="ctrl.meta.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.panelMeta.timeInfo}}</span>' +
           '</span>';
 
       function createExternalLinkMenu(ctrl) {
@@ -44,7 +44,7 @@ function (angular, $, _) {
         template += '<div class="panel-menu-row">';
         template += '<a class="panel-menu-link" gf-dropdown="extendedMenu"><i class="fa fa-bars"></i></a>';
 
-        _.each(ctrl.panelMeta.menu, function(item) {
+        _.each(ctrl.meta.menu, function(item) {
           // skip edit actions if not editor
           if (item.role === 'Editor' && !ctrl.dashboard.meta.canEdit) {
             return;
@@ -64,7 +64,7 @@ function (angular, $, _) {
       }
 
       function getExtendedMenu(ctrl) {
-        return angular.copy(ctrl.panelMeta.extendedMenu);
+        return angular.copy(ctrl.meta.extendedMenu);
       }
 
       return {
@@ -80,7 +80,7 @@ function (angular, $, _) {
 
           elem.append($link);
 
-          $scope.$watchCollection('panel.links', function(newValue) {
+          $scope.$watchCollection('ctrl.panel.links', function(newValue) {
             var showIcon = (newValue ? newValue.length > 0 : false) && ctrl.panel.title !== '';
             $panelLinksBtn.toggle(showIcon);
           });

+ 56 - 0
public/app/features/panel/panel_meta3.ts

@@ -0,0 +1,56 @@
+///<reference path="../../headers/common.d.ts" />
+
+import config from 'app/core/config';
+
+function panelOptionsTab() {
+  return {templateUrl: 'app/partials/panelgeneral.html'};
+}
+
+export default class PanelMeta {
+  description: any;
+  icon: any;
+  name: any;
+  menu: any;
+  editorTabs: any;
+  extendedMenu: any;
+
+  constructor(panel) {
+    let panelInfo = config.panels[panel.type];
+    console.log(panelInfo);
+
+    this.icon = panelInfo.icon;
+    this.name = panelInfo.name;
+    this.menu = [];
+    this.editorTabs = [];
+    this.extendedMenu = [];
+
+    if (panelInfo.fullscreen) {
+      this.addMenuItem('View', 'icon-eye-open', 'ctrl.viewPanel(); dismiss();');
+    }
+
+    this.addMenuItem('Edit', 'icon-cog', 'ctrl.editPanel(); dismiss();', 'Editor');
+    this.addMenuItem('Duplicate', 'icon-copy', 'ctrl.duplicate()', 'Editor');
+    this.addMenuItem('Share', 'icon-share', 'ctrl.share(); dismiss();');
+
+    this.addEditorTab('General', panelOptionsTab);
+
+    if (panelInfo.metricsEditor) {
+      this.addEditorTab('Metrics', 'app/partials/metrics.html');
+    }
+
+    this.addExtendedMenuItem('Panel JSON', '', 'ctrl.editPanelJson(); dismiss();');
+  }
+
+  addMenuItem (text, icon, click, role?) {
+    this.menu.push({text: text, icon: icon, click: click, role: role});
+  }
+
+  addExtendedMenuItem (text, icon, click, role?) {
+    this.extendedMenu.push({text: text, icon: icon, click: click, role: role});
+  }
+
+  addEditorTab(title, directiveFn) {
+    this.editorTabs.push({title: title, directiveFn: directiveFn});
+  }
+}
+

+ 6 - 6
public/app/features/panel/partials/panel.html

@@ -23,12 +23,12 @@
 	<div class="gf-box">
 		<div class="gf-box-header">
 			<div class="gf-box-title">
-				<i ng-class="panelMeta.editIcon"></i>
-				{{ctrl.panelMeta.panelName}}
+				<i ng-class="ctrl.meta.icon"></i>
+				{{ctrl.meta.name}}
 			</div>
 
-			<div ng-model="editor.index" bs-tabs>
-				<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
+			<div ng-model="ctrl.tabIndex" bs-tabs>
+				<div ng-repeat="tab in ctrl.meta.editorTabs" data-title="{{tab.title}}">
 				</div>
 			</div>
 
@@ -38,8 +38,8 @@
 		</div>
 
 		<div class="gf-box-body">
-			<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
-				<div ng-include src="tab.src"></div>
+			<div ng-repeat="tab in ctrl.meta.editorTabs" ng-if="ctrl.tabIndex === $index">
+				<panel-editor-tab editor-tab="tab" panel-ctrl="ctrl"></panel-editor-tab>
 			</div>
 		</div>
 	</div>

+ 7 - 7
public/app/partials/panelgeneral.html

@@ -7,23 +7,23 @@
 					Title
 				</li>
 				<li>
-					<input type="text" class="input-xlarge tight-form-input" ng-model='panel.title'></input>
+					<input type="text" class="input-xlarge tight-form-input" ng-model='panelCtrl.panel.title'></input>
 				</li>
 				<li class="tight-form-item">
 					Span
 				</li>
 				<li>
-					<select class="input-mini tight-form-input" ng-model="panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
+					<select class="input-mini tight-form-input" ng-model="panelCtrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
 				</li>
 				<li class="tight-form-item">
 					Height
 				</li>
 				<li>
-					<input type="text" class="input-small tight-form-input" ng-model='panel.height'></input>
+					<input type="text" class="input-small tight-form-input" ng-model='panelCtrl.panel.height'></input>
 				</li>
 				<li class="tight-form-item">
 					<label class="checkbox-label" for="panel.transparent">Transparent</label>
-					<input class="cr1" id="panel.transparent" type="checkbox" ng-model="panel.transparent" ng-checked="panel.transparent">
+					<input class="cr1" id="panel.transparent" type="checkbox" ng-model="panelCtrl.panel.transparent" ng-checked="panelCtrl.panel.transparent">
 					<label for="panel.transparent" class="cr1"></label>
 				</li>
 			</ul>
@@ -38,7 +38,7 @@
 					Repeat Panel
 				</li>
 				<li>
-					<select class="input-small tight-form-input last" ng-model="panel.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
+					<select class="input-small tight-form-input last" ng-model="panelCtrl.panel.repeat" ng-options="f.name as f.name for f in panelCtrl.dashboard.templating.list">
 						<option value=""></option>
 					</select>
 				</li>
@@ -46,7 +46,7 @@
 					Min span
 				</li>
 				<li>
-					<select class="input-small tight-form-input last" ng-model="panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
+					<select class="input-small tight-form-input last" ng-model="panelCtrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
 						<option value=""></option>
 					</select>
 				</li>
@@ -56,6 +56,6 @@
 	</div>
 </div>
 
-<panel-links-editor panel="panel"></panel-links-editor>
+<panel-links-editor panel="panelCtrl.panel"></panel-links-editor>
 
 

+ 16 - 8
public/app/plugins/panel/test/module.ts

@@ -1,6 +1,6 @@
 ///<reference path="../../../headers/common.d.ts" />
 
-import {PanelCtrl} from '../../../features/panel/panel_ctrl';
+import {PanelDirective, PanelCtrl} from '../../../features/panel/panel';
 
 class TestPanelCtrl extends PanelCtrl {
   constructor($scope) {
@@ -8,15 +8,23 @@ class TestPanelCtrl extends PanelCtrl {
   }
 }
 
-var panel = {
-  templateUrl: `app/plugins/panel/test/module.html`,
-  controller: TestPanelCtrl,
-  link: function(scope, elem) {
-    console.log('panel link');
+
+class TestPanel extends PanelDirective {
+  templateUrl = `app/plugins/panel/test/module.html`;
+  controller = TestPanelCtrl;
+
+  constructor($http) {
+    super();
+    console.log('panel ctor: ', $http);
   }
-};
+
+  link(scope) {
+    console.log('panel link: ', scope.ctrl.panel.id);
+  }
+}
 
 export {
   TestPanelCtrl,
-  panel,
+  // testPanelDirective as panel,
+  TestPanel as Panel,
 }

+ 14 - 1
public/app/plugins/panel/test/plugin.json

@@ -1,5 +1,18 @@
 {
   "type": "panel",
   "name": "Test",
-  "id": "test"
+  "id": "test",
+
+  "info": {
+    "description": "Test panel",
+    "author": {
+      "name": "Core Grafana Team.",
+      "url": "http://grafana.org"
+    },
+    "logos": {
+      "icon": "fa fa-fw th-large",
+      "small": "img/logo_small.png",
+      "large": "img/logo_large.png"
+    }
+  }
 }

+ 7 - 11
public/app/plugins/panel/unknown/module.ts

@@ -1,15 +1,11 @@
 ///<reference path="../../../headers/common.d.ts" />
 
-export function unknownPanelDirective() {
-  return {
-    restrict: 'E',
-    template: `
-    <grafana-panel>
-      <div class="text-center" style="padding-top: 2rem">
-          Unknown panel type: <strong>{{panel.type}}</strong>
-      </div>
-    </grafana-panel>
-    `,
-  };
+import {PanelDirective} from '../../../features/panel/panel';
+
+export class UnknownPanel extends PanelDirective {
+  template = `<div class="text-center" style="padding-top: 2rem">
+                Unknown panel type: <strong>{{ctrl.panel.type}}</strong>
+              </div>`;
 }
 
+