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
This commit is contained in:
@@ -97,6 +97,22 @@ export class KeybindingSrv {
|
|||||||
scope.appEvent('quick-snapshot');
|
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', () => {
|
this.bind('esc', () => {
|
||||||
var popups = $('.popover.in');
|
var popups = $('.popover.in');
|
||||||
if (popups.length > 0) {
|
if (popups.length > 0) {
|
||||||
|
|||||||
@@ -185,10 +185,20 @@ export class DashboardModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleEditMode() {
|
toggleEditMode() {
|
||||||
|
if (!this.meta.canEdit) {
|
||||||
|
console.log('Not allowed to edit dashboard');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.editMode = !this.editMode;
|
this.editMode = !this.editMode;
|
||||||
this.updateSubmenuVisibility();
|
this.updateSubmenuVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPanelFocus(id) {
|
||||||
|
console.log('setting focus panel id', id);
|
||||||
|
this.meta.focusPanelId = id;
|
||||||
|
}
|
||||||
|
|
||||||
updateSubmenuVisibility() {
|
updateSubmenuVisibility() {
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
this.meta.submenuEnabled = true;
|
this.meta.submenuEnabled = true;
|
||||||
|
|||||||
@@ -41,6 +41,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="!ctrl.dashboard.editMode">
|
<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">
|
<div class="dash-row-header" ng-if="ctrl.showtitle">
|
||||||
<a class="dash-row-header-title" ng-click="ctrl.toggleCollapse()">
|
<a class="dash-row-header-title" ng-click="ctrl.toggleCollapse()">
|
||||||
<span class="dash-row-collapse-toggle pointer">
|
<span class="dash-row-collapse-toggle pointer">
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class DashRowCtrl {
|
|||||||
// insert after
|
// insert after
|
||||||
dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
|
dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
|
||||||
// remove from source row
|
// remove from source row
|
||||||
dragObject.row.removePanel(dragObject.panel);
|
dragObject.row.removePanel(dragObject.panel, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dragObject.panel.span = 12 - this.row.span;
|
dragObject.panel.span = 12 - this.row.span;
|
||||||
@@ -66,7 +66,7 @@ export class DashRowCtrl {
|
|||||||
|
|
||||||
// if not new remove from source row
|
// if not new remove from source row
|
||||||
if (!dragObject.isNew) {
|
if (!dragObject.isNew) {
|
||||||
dragObject.row.removePanel(dragObject.panel);
|
dragObject.row.removePanel(dragObject.panel, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +104,20 @@ export class DashRowCtrl {
|
|||||||
showRowOptions() {
|
showRowOptions() {
|
||||||
this.dropView = this.dropView === 2 ? 0 : 2;
|
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) {
|
coreModule.directive('dashRow', function($rootScope) {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {Emitter, contextSrv} from 'app/core/core';
|
import {Emitter, contextSrv, appEvents, assignModelProperties} from 'app/core/core';
|
||||||
import {assignModelProperties} from 'app/core/core';
|
|
||||||
|
|
||||||
export class DashboardRow {
|
export class DashboardRow {
|
||||||
panels: any;
|
panels: any;
|
||||||
@@ -79,10 +78,23 @@ export class DashboardRow {
|
|||||||
this.panelSpanChanged();
|
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);
|
var index = _.indexOf(this.panels, panel);
|
||||||
this.panels.splice(index, 1);
|
this.panels.splice(index, 1);
|
||||||
|
|
||||||
this.events.emit('panel-removed', panel);
|
this.events.emit('panel-removed', panel);
|
||||||
this.panelSpanChanged();
|
this.panelSpanChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,15 +201,7 @@ export class PanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removePanel() {
|
removePanel() {
|
||||||
this.publishAppEvent('confirm-modal', {
|
this.row.removePanel(this.panel);
|
||||||
title: 'Remove Panel',
|
|
||||||
text: 'Are you sure you want to remove this panel?',
|
|
||||||
icon: 'fa-trash',
|
|
||||||
yesText: 'Remove',
|
|
||||||
onConfirm: () => {
|
|
||||||
this.row.removePanel(this.panel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editPanelJson() {
|
editPanelJson() {
|
||||||
|
|||||||
@@ -74,6 +74,16 @@ module.directive('grafanaPanel', function($rootScope) {
|
|||||||
var hasAlertRule;
|
var hasAlertRule;
|
||||||
var lastHeight = 0;
|
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
|
// set initial height
|
||||||
if (!ctrl.containerHeight) {
|
if (!ctrl.containerHeight) {
|
||||||
ctrl.calculatePanelHeight();
|
ctrl.calculatePanelHeight();
|
||||||
@@ -122,6 +132,13 @@ module.directive('grafanaPanel', function($rootScope) {
|
|||||||
lastFullscreen = ctrl.fullscreen;
|
lastFullscreen = ctrl.fullscreen;
|
||||||
}
|
}
|
||||||
}, scope);
|
}, scope);
|
||||||
|
|
||||||
|
panelContainer.on('mouseenter', mouseEnter);
|
||||||
|
panelContainer.on('mouseleave', mouseLeave);
|
||||||
|
|
||||||
|
scope.$on('$destroy', function() {
|
||||||
|
panelContainer.off();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<div class="confirm-modal-buttons">
|
<div class="confirm-modal-buttons">
|
||||||
<button type="button" class="btn btn-inverse" ng-click="dismiss()">{{noText}}</button>
|
<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>
|
<button ng-show="onAltAction" type="button" class="btn btn-success" ng-click="dismiss();onAltAction();">{{altActionText}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
|||||||
}, scope);
|
}, scope);
|
||||||
|
|
||||||
rootScope.onAppEvent('clearCrosshair', function() {
|
rootScope.onAppEvent('clearCrosshair', function() {
|
||||||
plot.clearCrosshair();
|
if (plot) {
|
||||||
|
plot.clearCrosshair();
|
||||||
|
}
|
||||||
}, scope);
|
}, scope);
|
||||||
|
|
||||||
// Receive render events
|
// Receive render events
|
||||||
@@ -535,7 +537,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
|||||||
return "%H:%M";
|
return "%H:%M";
|
||||||
}
|
}
|
||||||
|
|
||||||
new GraphTooltip(elem, dashboard, scope, function() {
|
var tooltip = new GraphTooltip(elem, dashboard, scope, function() {
|
||||||
return sortedSeries;
|
return sortedSeries;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -547,6 +549,12 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
scope.$on('$destroy', function() {
|
||||||
|
tooltip.destroy();
|
||||||
|
elem.off();
|
||||||
|
elem.remove();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ function ($, _) {
|
|||||||
|
|
||||||
var $tooltip = $('<div id="tooltip" class="graph-tooltip">');
|
var $tooltip = $('<div id="tooltip" class="graph-tooltip">');
|
||||||
|
|
||||||
|
this.destroy = function() {
|
||||||
|
$tooltip.remove();
|
||||||
|
};
|
||||||
|
|
||||||
this.findHoverIndexFromDataPoints = function(posX, series, last) {
|
this.findHoverIndexFromDataPoints = function(posX, series, last) {
|
||||||
var ps = series.datapoints.pointsize;
|
var ps = series.datapoints.pointsize;
|
||||||
var initial = last*ps;
|
var initial = last*ps;
|
||||||
|
|||||||
@@ -215,3 +215,45 @@ a.dash-row-header-actions--tight {
|
|||||||
width: 2rem;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -152,7 +152,11 @@ div.flot-text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel-highlight {
|
.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 {
|
.on-drag-hover {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
baseURL: '/base/',
|
baseURL: '/base/',
|
||||||
defaultJSExtensions: true,
|
defaultJSExtensions: true,
|
||||||
paths: {
|
paths: {
|
||||||
|
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
|
||||||
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
|
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
|
||||||
'tether': 'vendor/npm/tether/dist/js/tether.js',
|
'tether': 'vendor/npm/tether/dist/js/tether.js',
|
||||||
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
|
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
|
||||||
@@ -65,6 +66,10 @@
|
|||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
exports: 'EventEmitter'
|
exports: 'EventEmitter'
|
||||||
},
|
},
|
||||||
|
'vendor/npm/mousetrap/mousetrap.js': {
|
||||||
|
format: 'global',
|
||||||
|
exports: 'Mousetrap'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user