Browse Source

ux(dashboard): varius dashboard ux fixes and keybinding improvements, press 'e' while hovering over panel will open dashboard in edit mode, pressing 'd' will remove panel, #6442

Torkel Ödegaard 9 years ago
parent
commit
95e7ead89b

+ 16 - 0
public/app/core/services/keybindingSrv.ts

@@ -97,6 +97,22 @@ export class KeybindingSrv {
       scope.appEvent('quick-snapshot');
     });
 
+    this.bind('e', () => {
+      if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
+        this.$rootScope.appEvent('panel-change-view', {
+          fullscreen: true, edit: true, panelId: dashboard.meta.focusPanelId
+        });
+      }
+    });
+
+    this.bind('d', () => {
+      if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
+        var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
+        panelInfo.row.removePanel(panelInfo.panel);
+        dashboard.meta.focusPanelId = 0;
+      }
+    });
+
     this.bind('esc', () => {
       var popups = $('.popover.in');
       if (popups.length > 0) {

+ 10 - 0
public/app/features/dashboard/model.ts

@@ -185,10 +185,20 @@ export class DashboardModel {
   }
 
   toggleEditMode() {
+    if (!this.meta.canEdit) {
+      console.log('Not allowed to edit dashboard');
+      return;
+    }
+
     this.editMode = !this.editMode;
     this.updateSubmenuVisibility();
   }
 
+  setPanelFocus(id) {
+    console.log('setting focus panel id', id);
+    this.meta.focusPanelId = id;
+  }
+
   updateSubmenuVisibility() {
     if (this.editMode) {
       this.meta.submenuEnabled = true;

+ 19 - 0
public/app/features/dashboard/row/row.html

@@ -41,6 +41,25 @@
 </div>
 
 <div ng-if="!ctrl.dashboard.editMode">
+  <div class="row-open">
+    <div class='row-tab dropdown' ng-show="dashboardMeta.canEdit" ng-hide="dashboard.meta.fullscreen">
+      <span class="row-tab-button dropdown-toggle" data-toggle="dropdown">
+        <i class="fa fa-bars"></i>
+      </span>
+      <ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="drop1">
+        <li>
+          <a ng-click="ctrl.onMenuAddPanel()">Add Panel</a>
+        </li>
+        <li>
+          <a ng-click="ctrl.onMenuRowOptions()">Row Options</a>
+        </li>
+        <li>
+          <a ng-click="ctrl.onMenuDeleteRow()">Delete row</a>
+        </li>
+      </ul>
+    </div>
+  </div>
+
   <div class="dash-row-header" ng-if="ctrl.showtitle">
     <a class="dash-row-header-title" ng-click="ctrl.toggleCollapse()">
       <span class="dash-row-collapse-toggle pointer">

+ 16 - 2
public/app/features/dashboard/row/row_ctrl.ts

@@ -58,7 +58,7 @@ export class DashRowCtrl {
         // insert after
         dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
         // remove from source row
-        dragObject.row.removePanel(dragObject.panel);
+        dragObject.row.removePanel(dragObject.panel, false);
       }
     } else {
       dragObject.panel.span = 12 - this.row.span;
@@ -66,7 +66,7 @@ export class DashRowCtrl {
 
       // if not new remove from source row
       if (!dragObject.isNew) {
-        dragObject.row.removePanel(dragObject.panel);
+        dragObject.row.removePanel(dragObject.panel, false);
       }
     }
 
@@ -104,6 +104,20 @@ export class DashRowCtrl {
   showRowOptions() {
     this.dropView = this.dropView === 2 ? 0 : 2;
   }
+
+  onMenuAddPanel() {
+    this.dashboard.toggleEditMode();
+    this.dropView = 1;
+  }
+
+  onMenuRowOptions() {
+    this.dashboard.toggleEditMode();
+    this.dropView = 2;
+  }
+
+  onMenuDeleteRow() {
+    this.dashboard.removeRow(this.row);
+  }
 }
 
 coreModule.directive('dashRow', function($rootScope) {

+ 16 - 4
public/app/features/dashboard/row/row_model.ts

@@ -1,8 +1,7 @@
 ///<reference path="../../../headers/common.d.ts" />
 
 import _ from 'lodash';
-import {Emitter, contextSrv} from 'app/core/core';
-import {assignModelProperties} from 'app/core/core';
+import {Emitter, contextSrv, appEvents, assignModelProperties} from 'app/core/core';
 
 export class DashboardRow {
   panels: any;
@@ -79,10 +78,23 @@ export class DashboardRow {
     this.panelSpanChanged();
   }
 
-  removePanel(panel) {
+  removePanel(panel, ask?) {
+    console.log('remove panel');
+    if (ask !== false) {
+      appEvents.emit('confirm-modal', {
+        title: 'Remove Panel',
+        text: 'Are you sure you want to remove this panel?',
+        icon: 'fa-trash',
+        yesText: 'Remove',
+        onConfirm: () => {
+          this.removePanel(panel, false);
+        }
+      });
+      return;
+    }
+
     var index = _.indexOf(this.panels, panel);
     this.panels.splice(index, 1);
-
     this.events.emit('panel-removed', panel);
     this.panelSpanChanged();
   }

+ 1 - 9
public/app/features/panel/panel_ctrl.ts

@@ -201,15 +201,7 @@ export class PanelCtrl {
   }
 
   removePanel() {
-    this.publishAppEvent('confirm-modal', {
-      title: 'Remove Panel',
-      text: 'Are you sure you want to remove this panel?',
-      icon: 'fa-trash',
-      yesText: 'Remove',
-      onConfirm: () => {
-        this.row.removePanel(this.panel);
-      }
-    });
+    this.row.removePanel(this.panel);
   }
 
   editPanelJson() {

+ 17 - 0
public/app/features/panel/panel_directive.ts

@@ -74,6 +74,16 @@ module.directive('grafanaPanel', function($rootScope) {
       var hasAlertRule;
       var lastHeight = 0;
 
+      function mouseEnter() {
+        panelContainer.toggleClass('panel-hover-highlight', true);
+        ctrl.dashboard.setPanelFocus(ctrl.panel.id);
+      }
+
+      function mouseLeave() {
+        panelContainer.toggleClass('panel-hover-highlight', false);
+        ctrl.dashboard.setPanelFocus(0);
+      }
+
       // set initial height
       if (!ctrl.containerHeight) {
         ctrl.calculatePanelHeight();
@@ -122,6 +132,13 @@ module.directive('grafanaPanel', function($rootScope) {
           lastFullscreen = ctrl.fullscreen;
         }
       }, scope);
+
+      panelContainer.on('mouseenter', mouseEnter);
+      panelContainer.on('mouseleave', mouseLeave);
+
+      scope.$on('$destroy', function() {
+        panelContainer.off();
+      });
     }
   };
 });

+ 1 - 1
public/app/partials/confirm_modal.html

@@ -28,7 +28,7 @@
 
 		<div class="confirm-modal-buttons">
 			<button type="button" class="btn btn-inverse" ng-click="dismiss()">{{noText}}</button>
-			<button type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid">{{yesText}}</button>
+			<button type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid" give-focus="true">{{yesText}}</button>
 			<button ng-show="onAltAction" type="button" class="btn btn-success" ng-click="dismiss();onAltAction();">{{altActionText}}</button>
 		</div>
 	</div>

+ 10 - 2
public/app/plugins/panel/graph/graph.ts

@@ -59,7 +59,9 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
       }, scope);
 
       rootScope.onAppEvent('clearCrosshair', function() {
-        plot.clearCrosshair();
+        if (plot) {
+          plot.clearCrosshair();
+        }
       }, scope);
 
       // Receive render events
@@ -535,7 +537,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
         return "%H:%M";
       }
 
-      new GraphTooltip(elem, dashboard, scope, function() {
+      var tooltip = new GraphTooltip(elem, dashboard, scope, function() {
         return sortedSeries;
       });
 
@@ -547,6 +549,12 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
           });
         });
       });
+
+      scope.$on('$destroy', function() {
+        tooltip.destroy();
+        elem.off();
+        elem.remove();
+      });
     }
   };
 });

+ 4 - 0
public/app/plugins/panel/graph/graph_tooltip.js

@@ -12,6 +12,10 @@ function ($, _) {
 
     var $tooltip = $('<div id="tooltip" class="graph-tooltip">');
 
+    this.destroy = function() {
+      $tooltip.remove();
+    };
+
     this.findHoverIndexFromDataPoints = function(posX, series, last) {
       var ps = series.datapoints.pointsize;
       var initial = last*ps;

+ 42 - 0
public/sass/components/_row.scss

@@ -215,3 +215,45 @@ a.dash-row-header-actions--tight {
   width: 2rem;
 }
 
+
+// Legacy mode
+.row-tab {
+  .dropdown-menu-right {
+    top: 0;
+    left: 33px;
+  }
+}
+
+.row-tab-button {
+  padding: 0px;
+  cursor: pointer;
+  vertical-align: middle;
+  width: 30px;
+  height: 30px;
+  text-align: center;
+  display: inline-block;
+  line-height: 30px;
+  background: $btn-success-bg;
+  color: rgba(255,255,255,.90);
+}
+
+.row-button {
+  width: 24px;
+  float: left;
+  cursor: pointer;
+  line-height: 31px;
+  background-color: $blue-dark;
+}
+
+.row-open {
+  margin-top: 1px;
+  left: -22px;
+  position: absolute;
+  z-index: 100;
+  transition: .10s left;
+  transition-delay: .05s;
+
+  &:hover {
+    left: 0px;
+  }
+}

+ 5 - 1
public/sass/pages/_dashboard.scss

@@ -152,7 +152,11 @@ div.flot-text {
 }
 
 .panel-highlight  {
-  box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 5px rgba(82,168,236,10.8)
+  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 5px rgba(82,168,236,10.8)
+}
+
+.panel-hover-highlight  {
+  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 1px rgba(82,168,236,10.8)
 }
 
 .on-drag-hover {

+ 5 - 0
public/test/test-main.js

@@ -10,6 +10,7 @@
     baseURL: '/base/',
     defaultJSExtensions: true,
     paths: {
+      'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
       'eventemitter3': 'vendor/npm/eventemitter3/index.js',
       'tether': 'vendor/npm/tether/dist/js/tether.js',
       'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
@@ -65,6 +66,10 @@
         format: 'cjs',
         exports: 'EventEmitter'
       },
+      'vendor/npm/mousetrap/mousetrap.js': {
+        format: 'global',
+        exports: 'Mousetrap'
+      },
     }
   });