From 40ea014fa86a1a9c897650d167995e2dd45125e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 2 Nov 2016 12:55:58 +0100 Subject: [PATCH] ux(dashboar): added shortcut for build mode, switched keybinding lib to mousetrap, supports sequences so also added for Go to home dashboard, open search with starred dashbords prefiltered, go to profile, #6442 --- package.json | 1 + public/app/core/components/search/search.ts | 6 +- public/app/core/core.ts | 2 + public/app/core/services/keybindingSrv.ts | 118 ++++++++++++++++++ public/app/core/services/util_srv.ts | 9 +- public/app/features/dashboard/all.js | 1 - .../app/features/dashboard/dashboard_ctrl.ts | 4 +- public/app/features/dashboard/keybindings.js | 96 -------------- .../features/dashboard/partials/settings.html | 6 - public/app/headers/common.d.ts | 5 + public/app/plugins/panel/graph/graph.ts | 6 +- .../plugins/panel/graph/threshold_manager.ts | 4 +- public/app/system.conf.js | 5 + public/sass/_variables.dark.scss | 2 +- public/sass/components/_panel_graph.scss | 2 + tasks/options/copy.js | 1 + 16 files changed, 155 insertions(+), 113 deletions(-) create mode 100644 public/app/core/services/keybindingSrv.ts delete mode 100644 public/app/features/dashboard/keybindings.js diff --git a/package.json b/package.json index 2171afee4d7..40badc957ff 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "grunt-sync": "^0.4.1", "karma-sinon": "^1.0.3", "lodash": "^2.4.1", + "mousetrap": "^1.6.0", "remarkable": "^1.6.2", "sinon": "1.16.1", "systemjs-builder": "^0.15.13", diff --git a/public/app/core/components/search/search.ts b/public/app/core/components/search/search.ts index a581afe3fd3..65a250d533c 100644 --- a/public/app/core/components/search/search.ts +++ b/public/app/core/components/search/search.ts @@ -29,7 +29,7 @@ export class SearchCtrl { this.isOpen = this.ignoreClose; } - openSearch() { + openSearch(evt, payload) { if (this.isOpen) { this.isOpen = false; return; @@ -43,6 +43,10 @@ export class SearchCtrl { this.currentSearchId = 0; this.ignoreClose = true; + if (payload && payload.starred) { + this.query.starred = true; + } + this.$timeout(() => { this.ignoreClose = false; this.giveSearchFocus = this.giveSearchFocus + 1; diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 5d30bc6d061..2041f9e60d8 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -44,6 +44,7 @@ import appEvents from './app_events'; import colors from './utils/colors'; import {assignModelProperties} from './utils/model_utils'; import {contextSrv} from './services/context_srv'; +import {KeybindingSrv} from './services/keybindingSrv'; export { @@ -66,4 +67,5 @@ export { colors, assignModelProperties, contextSrv, + KeybindingSrv, }; diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts new file mode 100644 index 00000000000..55b1cfaadc2 --- /dev/null +++ b/public/app/core/services/keybindingSrv.ts @@ -0,0 +1,118 @@ +/// + +import $ from 'jquery'; + +import coreModule from 'app/core/core_module'; +import appEvents from 'app/core/app_events'; + +import Mousetrap from 'mousetrap'; + +export class KeybindingSrv { + helpModal: boolean; + + /** @ngInject */ + constructor(private $rootScope, private $modal, private $location) { + // clear out all shortcuts on route change + $rootScope.$on('$routeChangeSuccess', () => { + Mousetrap.reset(); + // rebind global shortcuts + this.setupGlobal(); + }); + + this.setupGlobal(); + } + + + setupGlobal() { + this.bind("?", this.showHelpModal); + this.bind("g h", this.goToHome); + this.bind("g p", this.goToProfile); + this.bind("s s", this.openSearchStarred); + this.bind(['f'], this.openSearch); + } + + openSearchStarred() { + this.$rootScope.appEvent('show-dash-search', {starred: true}); + } + + openSearch() { + this.$rootScope.appEvent('show-dash-search'); + } + + goToHome() { + this.$location.path("/"); + } + + goToProfile() { + this.$location.path("/profile"); + } + + showHelpModal() { + console.log('showing help modal'); + appEvents.emit('show-modal', { + src: 'public/app/partials/help_modal.html', + model: {} + }); + } + + bind(keyArg, fn) { + Mousetrap.bind(keyArg, evt => { + evt.preventDefault(); + evt.stopPropagation(); + return this.$rootScope.$apply(fn.bind(this)); + }); + } + + setupDashboardBindings(scope, dashboard) { + this.bind('b', () => { + dashboard.toggleEditMode(); + }); + + this.bind('ctrl+o', () => { + dashboard.sharedCrosshair = !dashboard.sharedCrosshair; + scope.broadcastRefresh(); + }); + + this.bind(['ctrl+s', 'command+s'], () => { + scope.appEvent('save-dashboard'); + }); + + this.bind('r', () => { + scope.broadcastRefresh(); + }); + + this.bind('ctrl+z', () => { + scope.appEvent('zoom-out'); + }); + + this.bind('left', () => { + scope.appEvent('shift-time-backward'); + }); + + this.bind('right', () => { + scope.appEvent('shift-time-forward'); + }); + + this.bind('ctrl+i', () => { + scope.appEvent('quick-snapshot'); + }); + + this.bind('esc', () => { + var popups = $('.popover.in'); + if (popups.length > 0) { + return; + } + // close modals + var modalData = $(".modal").data(); + if (modalData && modalData.$scope && modalData.$scope.dismiss) { + modalData.$scope.dismiss(); + } + + scope.appEvent('hide-dash-editor'); + + scope.exitFullscreen(); + }); + } +} + +coreModule.service('keybindingSrv', KeybindingSrv); diff --git a/public/app/core/services/util_srv.ts b/public/app/core/services/util_srv.ts index 8ca7bf8be72..ca386a491b9 100644 --- a/public/app/core/services/util_srv.ts +++ b/public/app/core/services/util_srv.ts @@ -8,6 +8,7 @@ import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; export class UtilSrv { + modalScope: any; /** @ngInject */ constructor(private $rootScope, private $modal) { @@ -18,9 +19,15 @@ export class UtilSrv { } showModal(options) { + if (this.modalScope && this.modalScope.dismiss) { + this.modalScope.dismiss(); + } + if (options.model) { - options.scope = this.$rootScope.$new(); + options.scope = this.modalScope = this.$rootScope.$new(); options.scope.model = options.model; + } else { + this.modalScope = options.scope; } var modal = this.$modal({ diff --git a/public/app/features/dashboard/all.js b/public/app/features/dashboard/all.js index edcc3c74d8e..88401000fb9 100644 --- a/public/app/features/dashboard/all.js +++ b/public/app/features/dashboard/all.js @@ -9,7 +9,6 @@ define([ './shareModalCtrl', './shareSnapshotCtrl', './dashboard_srv', - './keybindings', './viewStateSrv', './timeSrv', './unsavedChangesSrv', diff --git a/public/app/features/dashboard/dashboard_ctrl.ts b/public/app/features/dashboard/dashboard_ctrl.ts index 075e3dcaa8f..cf72bb87f61 100644 --- a/public/app/features/dashboard/dashboard_ctrl.ts +++ b/public/app/features/dashboard/dashboard_ctrl.ts @@ -13,7 +13,7 @@ export class DashboardCtrl { constructor( private $scope, private $rootScope, - dashboardKeybindings, + keybindingSrv, timeSrv, variableSrv, alertingSrv, @@ -61,7 +61,7 @@ export class DashboardCtrl { $scope.dashboardMeta = dashboard.meta; $scope.dashboardViewState = dashboardViewStateSrv.create($scope); - dashboardKeybindings.shortcuts($scope); + keybindingSrv.setupDashboardBindings($scope, dashboard); $scope.dashboard.updateSubmenuVisibility(); $scope.setWindowTitleAndTheme(); diff --git a/public/app/features/dashboard/keybindings.js b/public/app/features/dashboard/keybindings.js deleted file mode 100644 index c8f0e23df17..00000000000 --- a/public/app/features/dashboard/keybindings.js +++ /dev/null @@ -1,96 +0,0 @@ -define([ - 'angular', - 'jquery', -], -function(angular, $) { - "use strict"; - - var module = angular.module('grafana.services'); - - module.service('dashboardKeybindings', function($rootScope, keyboardManager, $modal, $q) { - - this.shortcuts = function(scope) { - - var unbindDestroy = scope.$on('$destroy', function() { - keyboardManager.unbindAll(); - unbindDestroy(); - }); - - var helpModalScope = null; - keyboardManager.bind('shift+?', function() { - if (helpModalScope) { return; } - - helpModalScope = $rootScope.$new(); - var helpModal = $modal({ - template: 'public/app/partials/help_modal.html', - persist: false, - show: false, - scope: helpModalScope, - keyboard: false - }); - - var unbindModalDestroy = helpModalScope.$on('$destroy', function() { - helpModalScope = null; - unbindModalDestroy(); - }); - - $q.when(helpModal).then(function(modalEl) { modalEl.modal('show'); }); - - }, { inputDisabled: true }); - - keyboardManager.bind('f', function() { - scope.appEvent('show-dash-search'); - }, { inputDisabled: true }); - - keyboardManager.bind('ctrl+o', function() { - var current = scope.dashboard.sharedCrosshair; - scope.dashboard.sharedCrosshair = !current; - scope.broadcastRefresh(); - }, { inputDisabled: true }); - - keyboardManager.bind('b', function() { - scope.dashboard.toggleEditMode(); - }, { inputDisabled: true }); - - keyboardManager.bind('ctrl+s', function(evt) { - scope.appEvent('save-dashboard', evt); - }, { inputDisabled: true }); - - keyboardManager.bind('r', function() { - scope.broadcastRefresh(); - }, { inputDisabled: true }); - - keyboardManager.bind('ctrl+z', function(evt) { - scope.appEvent('zoom-out', evt); - }, { inputDisabled: true }); - - keyboardManager.bind('left', function(evt) { - scope.appEvent('shift-time-backward', evt); - }, { inputDisabled: true }); - - keyboardManager.bind('right', function(evt) { - scope.appEvent('shift-time-forward', evt); - }, { inputDisabled: true }); - - keyboardManager.bind('ctrl+i', function(evt) { - scope.appEvent('quick-snapshot', evt); - }, { inputDisabled: true }); - - keyboardManager.bind('esc', function() { - var popups = $('.popover.in'); - if (popups.length > 0) { - return; - } - // close modals - var modalData = $(".modal").data(); - if (modalData && modalData.$scope && modalData.$scope.dismiss) { - modalData.$scope.dismiss(); - } - - scope.appEvent('hide-dash-editor'); - - scope.exitFullscreen(); - }, { inputDisabled: true }); - }; - }); -}); diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index 679b698502a..ad751c4a1bc 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -55,12 +55,6 @@ checked="dashboard.editable" label-class="width-11"> - - /vendor/npm' }