Commit 2fa7100c by Torkel Ödegaard

ux(dashboard): lots of tweaks and polish to row menu, added remove X to add…

ux(dashboard): lots of tweaks and polish to row menu, added remove X to add panel and row option views, #6442
parent 616e9ce5
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"eventemitter3": "^1.2.0", "eventemitter3": "^1.2.0",
"gaze": "^1.1.2",
"grunt-jscs": "~1.5.x", "grunt-jscs": "~1.5.x",
"grunt-sass-lint": "^0.2.0", "grunt-sass-lint": "^0.2.0",
"grunt-sync": "^0.4.1", "grunt-sync": "^0.4.1",
......
...@@ -66,6 +66,7 @@ export class KeybindingSrv { ...@@ -66,6 +66,7 @@ export class KeybindingSrv {
Mousetrap.bind(keyArg, evt => { Mousetrap.bind(keyArg, evt => {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
evt.returnValue = false;
return this.$rootScope.$apply(fn.bind(this)); return this.$rootScope.$apply(fn.bind(this));
}); });
} }
...@@ -80,31 +81,32 @@ export class KeybindingSrv { ...@@ -80,31 +81,32 @@ export class KeybindingSrv {
// dashboard.toggleEditMode(); // dashboard.toggleEditMode();
// }); // });
this.bind('ctrl+o', () => { this.bind('mod+o', () => {
dashboard.sharedCrosshair = !dashboard.sharedCrosshair; dashboard.sharedCrosshair = !dashboard.sharedCrosshair;
scope.broadcastRefresh(); scope.broadcastRefresh();
}); });
this.bind('ctrl+h', () => { this.bind('mod+h', () => {
dashboard.hideControls = !dashboard.hideControls; dashboard.hideControls = !dashboard.hideControls;
}); });
this.bind(['ctrl+s', 'command+s'], () => { this.bind('mod+s', e => {
scope.appEvent('save-dashboard'); scope.appEvent('save-dashboard');
}); });
this.bind('ctrl+z', () => {
this.bind('t z', () => {
scope.appEvent('zoom-out'); scope.appEvent('zoom-out');
}); });
this.bind('left', () => { this.bind('t left', () => {
scope.appEvent('shift-time-backward'); scope.appEvent('shift-time-backward');
}); });
this.bind('right', () => { this.bind('t right', () => {
scope.appEvent('shift-time-forward'); scope.appEvent('shift-time-forward');
}); });
this.bind('ctrl+i', () => { this.bind('mod+i', () => {
scope.appEvent('quick-snapshot'); scope.appEvent('quick-snapshot');
}); });
...@@ -133,7 +135,7 @@ export class KeybindingSrv { ...@@ -133,7 +135,7 @@ export class KeybindingSrv {
}); });
// delete panel // delete panel
this.bind('r', () => { this.bind('p r', () => {
if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) { if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId); var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
panelInfo.row.removePanel(panelInfo.panel); panelInfo.row.removePanel(panelInfo.panel);
...@@ -141,8 +143,26 @@ export class KeybindingSrv { ...@@ -141,8 +143,26 @@ export class KeybindingSrv {
} }
}); });
// delete panel // delete row
this.bind('s', () => { this.bind('r r', () => {
if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
dashboard.removeRow(panelInfo.row);
dashboard.meta.focusPanelId = 0;
}
});
// collapse row
this.bind('r c', () => {
if (dashboard.meta.focusPanelId) {
var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
panelInfo.row.toggleCollapse();
dashboard.meta.focusPanelId = 0;
}
});
// share panel
this.bind('p s', () => {
if (dashboard.meta.focusPanelId) { if (dashboard.meta.focusPanelId) {
var shareScope = scope.$new(); var shareScope = scope.$new();
var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId); var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
......
...@@ -23,20 +23,22 @@ export class UtilSrv { ...@@ -23,20 +23,22 @@ export class UtilSrv {
this.modalScope.dismiss(); this.modalScope.dismiss();
} }
if (options.model || !options.scope) {
options.scope = this.modalScope = this.$rootScope.$new();
options.scope.model = options.model;
}
this.modalScope = options.scope; this.modalScope = options.scope;
if (options.model) {
this.modalScope = this.$rootScope.$new();
this.modalScope.model = options.model;
} else if (!this.modalScope) {
this.modalScope = this.$rootScope.$new();
}
var modal = this.$modal({ var modal = this.$modal({
modalClass: options.modalClass, modalClass: options.modalClass,
template: options.src, template: options.src,
templateHtml: options.templateHtml, templateHtml: options.templateHtml,
persist: false, persist: false,
show: false, show: false,
scope: options.scope, scope: this.modalScope,
keyboard: false, keyboard: false,
backdrop: options.backdrop backdrop: options.backdrop
}); });
......
<div class="dash-row-add-panel"> <div class="dash-row-dropview">
<a class="dash-row-dropview-close pointer" ng-click="ctrl.rowCtrl.closeDropView();">
<i class="fa fa-remove"></i>
</a>
<div class="gf-form-inline dash-row-add-panel-form"> <div class="gf-form-inline dash-row-add-panel-form">
<div class="gf-form"> <div class="gf-form">
...@@ -12,7 +15,7 @@ ...@@ -12,7 +15,7 @@
ng-repeat="panel in ctrl.panelHits" ng-repeat="panel in ctrl.panelHits"
ng-class="{active: $index === ctrl.activeIndex}" ng-class="{active: $index === ctrl.activeIndex}"
ng-click="ctrl.addPanel(panel)" ng-click="ctrl.addPanel(panel)"
ui-draggable="ctrl.dashboard.editMode" ui-draggable="true"
drag="panel.id" drag="panel.id"
title="{{panel.name}}"> title="{{panel.name}}">
<img class="add-panel-item-img" ng-src="{{panel.info.logos.small}}"></img> <img class="add-panel-item-img" ng-src="{{panel.info.logos.small}}"></img>
......
<div class="dash-row-options"> <div class="dash-row-dropview">
<div class="gf-form section"> <a class="dash-row-dropview-close pointer" ng-click="ctrl.rowCtrl.closeDropView();">
<div class="gf-form-inline"> <i class="fa fa-remove"></i>
</a>
<div>
<div class="section">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-6">Row Title</span> <span class="gf-form-label width-6">Title</span>
<input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.title'></input> <input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.title'></input>
</div> </div>
<div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-6">Size</label> <label class="gf-form-label width-6">Size</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
...@@ -14,22 +19,16 @@ ...@@ -14,22 +19,16 @@
<gf-form-switch class="gf-form" label="Show" checked="ctrl.row.showTitle"> <gf-form-switch class="gf-form" label="Show" checked="ctrl.row.showTitle">
</gf-form-switch> </gf-form-switch>
</div> </div>
<div class="gf-form-inline"> </div>
<div class="section">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-6">Height</span> <span class="gf-form-label width-7">Height</span>
<input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.height'></input> <input type="text" class="gf-form-input max-width-14" ng-model='ctrl.row.height'></input>
</div> </div>
</div>
</div>
<div class="gf-form section">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label">Repeat Row</span> <span class="gf-form-label width-7">Repeat for</span>
<dash-repeat-option model="ctrl.row"></dash-repeat-option> <dash-repeat-option model="ctrl.row"></dash-repeat-option>
</div> </div>
</div> </div>
</div>
<div class="clearfix"></div>
</div> </div>
<div class="dash-row-menu-container" data-click-hide> <div class="dash-row-header" ng-if="ctrl.row.showTitle || ctrl.row.collapse">
<a class="dash-row-header-title" ng-click="ctrl.toggleCollapse()">
<span class="dash-row-collapse-toggle pointer">
<i class="fa fa-chevron-down" ng-show="!ctrl.row.collapse"></i>
<i class="fa fa-chevron-right" ng-show="ctrl.row.collapse"></i>
</span>
<span ng-class="ctrl.row.titleSize">{{ctrl.row.title | interpolateTemplateVars:this}}</span>
</a>
</div>
<div ng-if="ctrl.dropView === 1">
<dash-row-add-panel row-ctrl="ctrl"></dash-row-add-panel>
</div>
<div ng-if="ctrl.dropView === 2">
<dash-row-options row-ctrl="ctrl"></dash-row-options>
</div>
<div class="panels-wrapper" ng-if="!ctrl.row.collapse">
<div class="dash-row-menu-container" data-click-hide>
<ul class="dash-row-menu" role="menu"> <ul class="dash-row-menu" role="menu">
<li> <li>
<a ng-click="ctrl.toggleCollapse()">
<i class="fa fa-minus"></i> Collapse
</a>
</li>
<li ng-show="ctrl.dashboard.meta.canEdit">
<a ng-click="ctrl.onMenuAddPanel()"> <a ng-click="ctrl.onMenuAddPanel()">
<i class="fa fa-plus"></i> Add Panel <i class="fa fa-plus"></i> Add Panel
</a> </a>
</li> </li>
<li> <li ng-show="ctrl.dashboard.meta.canEdit">
<a ng-click="ctrl.onMenuRowOptions()"> <a ng-click="ctrl.onMenuRowOptions()">
<i class="fa fa-cog"></i> Row Options <i class="fa fa-cog"></i> Row Options
</a> </a>
</li> </li>
<li> <li ng-show="ctrl.dashboard.meta.canEdit">
<a ng-click="ctrl.onMenuDeleteRow()"> <a ng-click="ctrl.moveRow(-1)">
<i class="fa fa-arrow-up"></i> Move Up <i class="fa fa-arrow-up"></i> Move Up
</a> </a>
</li> </li>
<li> <li ng-show="ctrl.dashboard.meta.canEdit">
<a ng-click="ctrl.onMenuDeleteRow()"> <a ng-click="ctrl.moveRow(1)">
<i class="fa fa-arrow-down"></i> Move Down <i class="fa fa-arrow-down"></i> Move Down
</a> </a>
</li> </li>
<li> <li ng-show="ctrl.dashboard.meta.canEdit">
<a ng-click="ctrl.onMenuDeleteRow()"> <a ng-click="ctrl.onMenuDeleteRow()">
<i class="fa fa-trash"></i> Remove row <i class="fa fa-trash"></i> Remove
</a> </a>
</li> </li>
</ul> </ul>
<div class="dash-row-menu-grip"> <div class="dash-row-menu-grip">
<i class="fa fa-ellipsis-v"></i> <i class="fa fa-ellipsis-v"></i>
</div> </div>
</div> </div>
<div class="dash-row-header" ng-if="ctrl.row.showTitle || ctrl.row.collapse">
<a class="dash-row-header-title" ng-click="ctrl.toggleCollapse()">
<span class="dash-row-collapse-toggle pointer">
<i class="fa fa-chevron-down" ng-show="!ctrl.row.collapse"></i>
<i class="fa fa-chevron-right" ng-show="ctrl.row.collapse"></i>
</span>
<span ng-class="ctrl.row.titleSize">{{ctrl.row.title | interpolateTemplateVars:this}}</span>
</a>
</div>
<div ng-if="ctrl.dropView === 1">
<dash-row-add-panel row-ctrl="ctrl"></dash-row-add-panel>
</div>
<div ng-if="ctrl.dropView === 2">
<dash-row-options row-ctrl="ctrl"></dash-row-options>
</div>
<div class="panels-wrapper" ng-if="!ctrl.row.collapse">
<div ng-repeat="panel in ctrl.row.panels track by panel.id" class="panel" ui-draggable="!ctrl.dashboard.meta.fullscreen" drag="panel.id" ui-on-drop="ctrl.onDrop($data, panel)" drag-handle-class="drag-handle" panel-width> <div ng-repeat="panel in ctrl.row.panels track by panel.id" class="panel" ui-draggable="!ctrl.dashboard.meta.fullscreen" drag="panel.id" ui-on-drop="ctrl.onDrop($data, panel)" drag-handle-class="drag-handle" panel-width>
<plugin-component type="panel" class="panel-margin"> <plugin-component type="panel" class="panel-margin">
</plugin-component> </plugin-component>
......
...@@ -127,6 +127,7 @@ coreModule.directive('dashRow', function($rootScope) { ...@@ -127,6 +127,7 @@ coreModule.directive('dashRow', function($rootScope) {
}, },
link: function(scope, element) { link: function(scope, element) {
scope.$watchGroup(['ctrl.row.collapse', 'ctrl.row.height'], function() { scope.$watchGroup(['ctrl.row.collapse', 'ctrl.row.height'], function() {
element.toggleClass('dash-row--collapse', scope.ctrl.row.collapse);
element.find('.panels-wrapper').css({minHeight: scope.ctrl.row.collapse ? '5px' : scope.ctrl.row.height}); element.find('.panels-wrapper').css({minHeight: scope.ctrl.row.collapse ? '5px' : scope.ctrl.row.height});
}); });
......
...@@ -11,6 +11,7 @@ export class DashboardRow { ...@@ -11,6 +11,7 @@ export class DashboardRow {
events: Emitter; events: Emitter;
span: number; span: number;
height: number; height: number;
collapse: boolean;
defaults = { defaults = {
title: 'Dashboard Row', title: 'Dashboard Row',
...@@ -22,6 +23,7 @@ export class DashboardRow { ...@@ -22,6 +23,7 @@ export class DashboardRow {
repeat: null, repeat: null,
repeatRowId: null, repeatRowId: null,
repeatIteration: null, repeatIteration: null,
collapse: false,
}; };
constructor(private model) { constructor(private model) {
...@@ -79,7 +81,6 @@ export class DashboardRow { ...@@ -79,7 +81,6 @@ export class DashboardRow {
} }
removePanel(panel, ask?) { removePanel(panel, ask?) {
console.log('remove panel');
if (ask !== false) { if (ask !== false) {
appEvents.emit('confirm-modal', { appEvents.emit('confirm-modal', {
title: 'Remove Panel', title: 'Remove Panel',
...@@ -113,5 +114,9 @@ export class DashboardRow { ...@@ -113,5 +114,9 @@ export class DashboardRow {
this.showTitle = source.showTitle; this.showTitle = source.showTitle;
this.titleSize = source.titleSize; this.titleSize = source.titleSize;
} }
toggleCollapse() {
this.collapse = !this.collapse;
}
} }
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
.page-container { .page-container {
padding: ($spacer * 1) ($spacer * 2); padding: ($spacer * 1) ($spacer * 2);
} }
.dash-row-menu-container {
display: none;
}
} }
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
......
...@@ -4,6 +4,14 @@ ...@@ -4,6 +4,14 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
&--collapse {
.dash-row-header {
background: $panel-bg;
border: $panel-border;
margin-bottom: $panel-margin*2;
}
}
} }
.dash-row-header { .dash-row-header {
...@@ -46,19 +54,21 @@ ...@@ -46,19 +54,21 @@
position: relative; position: relative;
} }
.dash-row-options { .dash-row-dropview {
position: relative;
background: $panel-bg; background: $panel-bg;
border: 1px solid $dash-row-border-color; border: 1px solid $dash-row-border-color;
margin: 0 $panel-margin $panel-margin*2 $panel-margin; margin: 0 $panel-margin $panel-margin*2 $panel-margin;
padding: $panel-margin*2; padding: $panel-margin*2;
display: flex;
} }
.dash-row-add-panel { .dash-row-dropview-close {
background: $panel-bg; position: absolute;
border: 1px solid $dash-row-border-color; right: -15px;
margin: 0 $panel-margin $panel-margin*2 $panel-margin; top: -12px;
padding: $panel-margin*2; width: 20px;
display: flex; height: 20px;
} }
.add-panel-panels-scroll { .add-panel-panels-scroll {
...@@ -80,7 +90,7 @@ ...@@ -80,7 +90,7 @@
.add-panel-item { .add-panel-item {
background: $input-label-bg; background: $input-label-bg;
border: $panel-border; border: $panel-border;
padding: $spacer; padding: $spacer/3 $spacer;
min-width: 9rem; min-width: 9rem;
max-width: 9rem; max-width: 9rem;
text-align: center; text-align: center;
...@@ -89,7 +99,7 @@ ...@@ -89,7 +99,7 @@
&.active, &.active,
&:hover { &:hover {
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 1px rgba(82,168,236,5.8)
} }
} }
...@@ -109,6 +119,8 @@ $dash-row-menu-animation-speed: 0.20s; ...@@ -109,6 +119,8 @@ $dash-row-menu-animation-speed: 0.20s;
.dash-row-menu-container { .dash-row-menu-container {
position: absolute; position: absolute;
top: 0px; top: 0px;
width: 138px;
height: 100%;
transform: translate(-131px, 0); transform: translate(-131px, 0);
transition: 0.1s ease-out 0.4s; transition: 0.1s ease-out 0.4s;
z-index: 100; z-index: 100;
...@@ -128,8 +140,6 @@ $dash-row-menu-animation-speed: 0.20s; ...@@ -128,8 +140,6 @@ $dash-row-menu-animation-speed: 0.20s;
} }
.dash-row-menu { .dash-row-menu {
border-top: $panel-border;
border-bottom: $panel-border;
list-style: none; list-style: none;
flex-grow: 1; flex-grow: 1;
box-shadow: $search-shadow; box-shadow: $search-shadow;
......
...@@ -179,7 +179,7 @@ ...@@ -179,7 +179,7 @@
top: 38%; top: 38%;
right: 6px; right: 6px;
font-size: 14px; font-size: 14px;
color: $text-color-weak; color: $text-color-faint;
} }
.sidemenu-org-avatar, .sidemenu-org-avatar,
......
...@@ -35,6 +35,9 @@ ...@@ -35,6 +35,9 @@
.dash-row-menu { .dash-row-menu {
display: none; display: none;
} }
.panel-drop-zone {
visibility: hidden;
}
} }
div.flot-text { div.flot-text {
......
module.exports = function(config, grunt) { module.exports = function(config, grunt) {
'use strict'; 'use strict';
grunt.event.on('watch', function(action, filepath) { var gaze = require('gaze');
var newPath; var path = require('path');
var firstRun = true;
var done;
var lastTime;
grunt.registerTask('watch', function() {
done = this.async();
lastTime = new Date().getTime();
if (firstRun === false) {
grunt.log.writeln('Watch resuming');
return;
}
gaze(config.srcDir + '/**/*', function(err, watcher) {
console.log('Gaze watchers setup');
watcher.on('all', function(evtName, filepath) {
filepath = path.relative(process.cwd(), filepath);
grunt.log.writeln('File Changed: ' + filepath); // ignore multiple changes at once
var now = new Date().getTime();
if (now - lastTime < 100) {
return;
}
lastTime = now;
var newPath;
grunt.log.writeln('File Changed: ', filepath);
if (/(\.html)|(\.json)$/.test(filepath)) { if (/(\.html)|(\.json)$/.test(filepath)) {
newPath = filepath.replace(/^public/, 'public_gen'); newPath = filepath.replace(/^public/, 'public_gen');
...@@ -39,15 +67,11 @@ module.exports = function(config, grunt) { ...@@ -39,15 +67,11 @@ module.exports = function(config, grunt) {
grunt.task.run('typescript:build'); grunt.task.run('typescript:build');
grunt.task.run('tslint'); grunt.task.run('tslint');
} }
});
return { done();
copy_to_gen: { firstRun = false;
files: ['<%= srcDir %>/**/*'], grunt.task.run('watch');
tasks: [], });
options: { });
spawn: false });
}
},
};
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment