Forráskód Böngészése

wip: angular panels now have similar edit mode and panel type selection enabling quick changing between panel react and angular panel types

Torkel Ödegaard 7 éve
szülő
commit
fd81f89509

+ 14 - 20
public/app/core/services/dynamic_directive_srv.ts

@@ -3,7 +3,7 @@ import coreModule from '../core_module';
 
 class DynamicDirectiveSrv {
   /** @ngInject */
-  constructor(private $compile, private $rootScope) {}
+  constructor(private $compile) {}
 
   addDirective(element, name, scope) {
     var child = angular.element(document.createElement(name));
@@ -14,25 +14,19 @@ class DynamicDirectiveSrv {
   }
 
   link(scope, elem, attrs, options) {
-    options
-      .directive(scope)
-      .then(directiveInfo => {
-        if (!directiveInfo || !directiveInfo.fn) {
-          elem.empty();
-          return;
-        }
-
-        if (!directiveInfo.fn.registered) {
-          coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
-          directiveInfo.fn.registered = true;
-        }
-
-        this.addDirective(elem, directiveInfo.name, scope);
-      })
-      .catch(err => {
-        console.log('Plugin load:', err);
-        this.$rootScope.appEvent('alert-error', ['Plugin error', err.toString()]);
-      });
+    const directiveInfo = options.directive(scope);
+    if (!directiveInfo || !directiveInfo.fn) {
+      elem.empty();
+      return;
+    }
+
+    if (!directiveInfo.fn.registered) {
+      console.log('register panel tab');
+      coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
+      directiveInfo.fn.registered = true;
+    }
+
+    this.addDirective(elem, directiveInfo.name, scope);
   }
 
   create(options) {

+ 5 - 0
public/app/features/dashboard/dashboard_model.ts

@@ -228,6 +228,11 @@ export class DashboardModel {
     return this.meta.fullscreen && !panel.fullscreen;
   }
 
+  changePanelType(panel: PanelModel, pluginId: string) {
+    panel.changeType(pluginId);
+    this.events.emit('panel-type-changed', panel);
+  }
+
   private ensureListExist(data) {
     if (!data) {
       data = {};

+ 2 - 1
public/app/features/dashboard/dashgrid/DashboardGrid.tsx

@@ -84,6 +84,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
     dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this));
     dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this));
     dashboard.on('row-expanded', this.triggerForceUpdate.bind(this));
+    dashboard.on('panel-type-changed', this.triggerForceUpdate.bind(this));
   }
 
   buildLayout() {
@@ -177,7 +178,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
       const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen });
       panelElements.push(
         <div key={panel.id.toString()} className={panelClasses}>
-          <DashboardPanel panel={panel} dashboard={this.props.dashboard} />
+          <DashboardPanel panel={panel} dashboard={this.props.dashboard} panelType={panel.type} />
         </div>
       );
     }

+ 13 - 2
public/app/features/dashboard/dashgrid/DashboardPanel.tsx

@@ -11,6 +11,7 @@ import { PanelChrome } from './PanelChrome';
 import { PanelEditor } from './PanelEditor';
 
 export interface Props {
+  panelType: string;
   panel: PanelModel;
   dashboard: DashboardModel;
 }
@@ -53,6 +54,10 @@ export class DashboardPanel extends React.Component<Props, State> {
     this.loadPlugin();
   };
 
+  onAngularPluginTypeChanged = () => {
+    this.loadPlugin();
+  };
+
   loadPlugin() {
     if (this.isSpecial()) {
       return;
@@ -63,9 +68,11 @@ export class DashboardPanel extends React.Component<Props, State> {
       this.pluginInfo = config.panels[this.props.panel.type];
 
       if (this.pluginInfo.exports) {
+        this.cleanUpAngularPanel();
         this.setState({ pluginExports: this.pluginInfo.exports });
       } else {
         importPluginModule(this.pluginInfo.module).then(pluginExports => {
+          this.cleanUpAngularPanel();
           // cache plugin exports (saves a promise async cycle next time)
           this.pluginInfo.exports = pluginExports;
           // update panel state
@@ -80,7 +87,6 @@ export class DashboardPanel extends React.Component<Props, State> {
   }
 
   componentDidUpdate() {
-    console.log('componentDidUpdate');
     this.loadPlugin();
 
     // handle angular plugin loading
@@ -94,12 +100,17 @@ export class DashboardPanel extends React.Component<Props, State> {
     this.angularPanel = loader.load(this.element, scopeProps, template);
   }
 
-  componentWillUnmount() {
+  cleanUpAngularPanel() {
     if (this.angularPanel) {
       this.angularPanel.destroy();
+      this.angularPanel = null;
     }
   }
 
+  componentWillUnmount() {
+    this.cleanUpAngularPanel();
+  }
+
   renderReactPanel() {
     const { pluginExports } = this.state;
     const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';

+ 3 - 4
public/app/features/dashboard/dashgrid/PanelEditor.tsx

@@ -31,7 +31,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
 
     this.tabs = [
       { id: 'queries', text: 'Queries', icon: 'fa fa-database' },
-      { id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' },
+      { id: 'visualization', text: 'Visualization', icon: 'fa fa-line-chart' },
     ];
   }
 
@@ -87,7 +87,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
 
         <div className="tabbed-view-body">
           {activeTab === 'queries' && this.renderQueriesTab()}
-          {activeTab === 'viz' && this.renderVizTab()}
+          {activeTab === 'visualization' && this.renderVizTab()}
         </div>
       </div>
     );
@@ -109,8 +109,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) {
   return (
     <li className="gf-tabs-item" key={tab.id}>
       <a className={tabClasses} onClick={() => onClick(tab)}>
-        <i className={tab.icon} />
-        {tab.text}
+        <i className={tab.icon} /> {tab.text}
       </a>
     </li>
   );

+ 6 - 3
public/app/features/dashboard/panel_model.ts

@@ -34,6 +34,7 @@ export class PanelModel {
   soloMode?: boolean;
   targets: any[];
   datasource: string;
+  thresholds?: any;
 
   // non persisted
   fullscreen: boolean;
@@ -116,9 +117,11 @@ export class PanelModel {
     this.events.emit('panel-init-edit-mode');
   }
 
-  changeType(newType: string) {
-    this.type = newType;
-    this.events.emit('panel-size-changed');
+  changeType(pluginId: string) {
+    this.type = pluginId;
+
+    delete this.thresholds;
+    delete this.alert;
   }
 
   destroy() {

+ 3 - 3
public/app/features/dashboard/view_state_srv.ts

@@ -16,7 +16,7 @@ export class DashboardViewState {
   oldTimeRange: any;
 
   /** @ngInject */
-  constructor($scope, private $location, private $timeout, private $rootScope) {
+  constructor($scope, private $location, private $timeout) {
     var self = this;
     self.state = {};
     self.panelScopes = [];
@@ -176,10 +176,10 @@ export class DashboardViewState {
 }
 
 /** @ngInject */
-export function dashboardViewStateSrv($location, $timeout, $rootScope) {
+export function dashboardViewStateSrv($location, $timeout) {
   return {
     create: function($scope) {
-      return new DashboardViewState($scope, $location, $timeout, $rootScope);
+      return new DashboardViewState($scope, $location, $timeout);
     },
   };
 }

+ 0 - 3
public/app/features/panel/metrics_panel_ctrl.ts

@@ -8,8 +8,6 @@ import * as rangeUtil from 'app/core/utils/rangeutil';
 import * as dateMath from 'app/core/utils/datemath';
 import { encodePathComponent } from 'app/core/utils/location_util';
 
-import { metricsTabDirective } from './metrics_tab';
-
 class MetricsPanelCtrl extends PanelCtrl {
   scope: any;
   datasource: any;
@@ -58,7 +56,6 @@ class MetricsPanelCtrl extends PanelCtrl {
   }
 
   private onInitMetricsPanelEditMode() {
-    this.addEditorTab('Metrics', metricsTabDirective);
     this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html');
   }
 

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

@@ -6,6 +6,8 @@ import { PanelModel } from 'app/features/dashboard/panel_model';
 import Remarkable from 'remarkable';
 import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants';
 import store from 'app/core/store';
+import { metricsTabDirective } from './metrics_tab';
+import { vizTabDirective } from './viz_tab';
 
 const TITLE_HEIGHT = 27;
 const PANEL_BORDER = 2;
@@ -97,7 +99,10 @@ export class PanelCtrl {
 
   initEditMode() {
     this.editorTabs = [];
+    this.addEditorTab('Queries', metricsTabDirective, 0, 'fa fa-database');
+    this.addEditorTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart');
     this.addEditorTab('General', 'public/app/partials/panelgeneral.html');
+
     this.editModeInitiated = true;
     this.events.emit('init-edit-mode', null);
 
@@ -118,8 +123,8 @@ export class PanelCtrl {
     route.updateParams();
   }
 
-  addEditorTab(title, directiveFn, index?) {
-    var editorTab = { title, directiveFn };
+  addEditorTab(title, directiveFn, index?, icon?) {
+    var editorTab = { title, directiveFn, icon };
 
     if (_.isString(directiveFn)) {
       editorTab.directiveFn = function() {

+ 2 - 4
public/app/features/panel/panel_directive.ts

@@ -32,13 +32,11 @@ var panelTemplate = `
                                                  'panel-height-helper': !ctrl.panel.isEditing}">
       <div class="tabbed-view tabbed-view--new">
         <div class="tabbed-view-header">
-          <h3 class="tabbed-view-panel-title">
-            {{ctrl.pluginName}}
-          </h3>
 
           <ul class="gf-tabs">
             <li class="gf-tabs-item" ng-repeat="tab in ::ctrl.editorTabs">
               <a class="gf-tabs-link" ng-click="ctrl.changeTab($index)" ng-class="{active: ctrl.editorTabIndex === $index}">
+                <i class="{{::tab.icon}}" ng-show="tab.icon"></i>
                 {{::tab.title}}
               </a>
             </li>
@@ -50,7 +48,7 @@ var panelTemplate = `
         </div>
 
         <div class="tabbed-view-body">
-          <div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index">
+          <div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index" class="panel-height-helper">
             <panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
           </div>
         </div>

+ 19 - 11
public/app/features/panel/panel_editor_tab.ts

@@ -1,6 +1,7 @@
 import angular from 'angular';
 
-var directiveModule = angular.module('grafana.directives');
+const directiveModule = angular.module('grafana.directives');
+const directiveCache = {};
 
 /** @ngInject */
 function panelEditorTab(dynamicDirectiveSrv) {
@@ -11,18 +12,25 @@ function panelEditorTab(dynamicDirectiveSrv) {
       index: '=',
     },
     directive: scope => {
-      var pluginId = scope.ctrl.pluginId;
-      var tabIndex = scope.index;
-      // create a wrapper for directiveFn
-      // required for metrics tab directive
-      // that is the same for many panels but
-      // given different names in this function
-      var fn = () => scope.editorTab.directiveFn();
+      const pluginId = scope.ctrl.pluginId;
+      const tabIndex = scope.index;
 
-      return Promise.resolve({
+      if (directiveCache[pluginId]) {
+        if (directiveCache[pluginId][tabIndex]) {
+          return directiveCache[pluginId][tabIndex];
+        }
+      } else {
+        directiveCache[pluginId] = [];
+      }
+
+      let result = {
+        fn: () => scope.editorTab.directiveFn(),
         name: `panel-editor-tab-${pluginId}${tabIndex}`,
-        fn: fn,
-      });
+      };
+
+      directiveCache[pluginId][tabIndex] = result;
+
+      return result;
     },
   });
 }

+ 48 - 0
public/app/features/panel/viz_tab.ts

@@ -0,0 +1,48 @@
+import coreModule from 'app/core/core_module';
+import { DashboardModel } from '../dashboard/dashboard_model';
+import { VizTypePicker } from '../dashboard/dashgrid/VizTypePicker';
+import { react2AngularDirective } from 'app/core/utils/react2angular';
+import { PanelPlugin } from 'app/types/plugins';
+
+export class VizTabCtrl {
+  panelCtrl: any;
+  dashboard: DashboardModel;
+
+  /** @ngInject */
+  constructor($scope) {
+    this.panelCtrl = $scope.ctrl;
+    this.dashboard = this.panelCtrl.dashboard;
+
+    $scope.ctrl = this;
+  }
+
+  onTypeChanged = (plugin: PanelPlugin) => {
+    this.dashboard.changePanelType(this.panelCtrl.panel, plugin.id);
+  };
+}
+
+let template = `
+<div class="viz-editor">
+  <div class="viz-editor-col1">
+    <viz-type-picker currentType="ctrl.panelCtrl.panel.type" onTypeChanged="ctrl.onTypeChanged"></viz-type-picker>
+  </div>
+  <div class="viz-editor-col2">
+    <h5 class="page-heading">Options</h5>
+  </div>
+</div>
+`;
+
+/** @ngInject **/
+export function vizTabDirective() {
+  'use strict';
+  return {
+    restrict: 'E',
+    scope: true,
+    template: template,
+    controller: VizTabCtrl,
+  };
+}
+
+react2AngularDirective('vizTypePicker', VizTypePicker, ['currentType', ['onTypeChanged', { watchDepth: 'reference' }]]);
+
+coreModule.directive('vizTab', vizTabDirective);

+ 1 - 0
public/app/features/plugins/plugin_component.ts

@@ -211,6 +211,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
       scope.$applyAsync(function() {
         scope.$broadcast('component-did-mount');
         scope.$broadcast('refresh');
+        console.log('appendAndCompile', scope.panel);
       });
     });
   }