Procházet zdrojové kódy

feat(sidemenu): added handling of click outside to hide sidemenu, also refactored grafana_ctrl to a more general grafana component

Torkel Ödegaard před 10 roky
rodič
revize
c201f4c63e

+ 184 - 0
public/app/core/components/grafana_app.ts

@@ -0,0 +1,184 @@
+///<reference path="../../headers/common.d.ts" />
+
+import config from 'app/core/config';
+import store from 'app/core/store';
+import _ from 'lodash';
+import angular from 'angular';
+import $ from 'jquery';
+import coreModule from '../core_module';
+
+export class GrafanaCtrl {
+
+  /** @ngInject */
+  constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv) {
+
+    $scope.init = function() {
+      $scope.contextSrv = contextSrv;
+
+      $scope._ = _;
+
+      $rootScope.profilingEnabled = store.getBool('profilingEnabled');
+      $rootScope.performance = { loadStart: new Date().getTime() };
+      $rootScope.appSubUrl = config.appSubUrl;
+
+      if ($rootScope.profilingEnabled) { $scope.initProfiling(); }
+
+      alertSrv.init();
+      utilSrv.init();
+
+      $scope.dashAlerts = alertSrv;
+    };
+
+    $scope.initDashboard = function(dashboardData, viewScope) {
+      $controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
+    };
+
+    $rootScope.onAppEvent = function(name, callback, localScope) {
+      var unbind = $rootScope.$on(name, callback);
+      var callerScope = this;
+      if (callerScope.$id === 1 && !localScope) {
+        console.log('warning rootScope onAppEvent called without localscope');
+      }
+      if (localScope) {
+        callerScope = localScope;
+      }
+      callerScope.$on('$destroy', unbind);
+    };
+
+    $rootScope.appEvent = function(name, payload) {
+      $rootScope.$emit(name, payload);
+    };
+
+    $rootScope.colors = [
+      "#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0",
+      "#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477",
+      "#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0",
+      "#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93",
+      "#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7",
+      "#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B",
+      "#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7"
+    ];
+
+    $scope.getTotalWatcherCount = function() {
+      var count = 0;
+      var scopes = 0;
+      var root = $(document.getElementsByTagName('body'));
+
+      var f = function (element) {
+        if (element.data().hasOwnProperty('$scope')) {
+          scopes++;
+          angular.forEach(element.data().$scope.$$watchers, function () {
+            count++;
+          });
+        }
+
+        angular.forEach(element.children(), function (childElement) {
+          f($(childElement));
+        });
+      };
+
+      f(root);
+      $rootScope.performance.scopeCount = scopes;
+      return count;
+    };
+
+    $scope.initProfiling = function() {
+      var count = 0;
+
+      $scope.$watch(function digestCounter() {
+        count++;
+      }, function() {
+        // something
+      });
+
+      $rootScope.performance.panels = [];
+
+      $scope.$on('refresh', function() {
+        if ($rootScope.performance.panels.length > 0) {
+          var totalRender = 0;
+          var totalQuery = 0;
+
+          _.each($rootScope.performance.panels, function(panelTiming: any) {
+            totalRender += panelTiming.render;
+            totalQuery += panelTiming.query;
+          });
+
+          console.log('total query: ' + totalQuery);
+          console.log('total render: ' + totalRender);
+          console.log('avg render: ' + totalRender / $rootScope.performance.panels.length);
+        }
+
+        $rootScope.performance.panels = [];
+      });
+
+      $scope.onAppEvent('dashboard-loaded', function() {
+        count = 0;
+
+        setTimeout(function() {
+          console.log("Dashboard::Performance Total Digests: " + count);
+          console.log("Dashboard::Performance Total Watchers: " + $scope.getTotalWatcherCount());
+          console.log("Dashboard::Performance Total ScopeCount: " + $rootScope.performance.scopeCount);
+
+          var timeTaken = $rootScope.performance.allPanelsInitialized - $rootScope.performance.dashboardLoadStart;
+          console.log("Dashboard::Performance - All panels initialized in " + timeTaken + " ms");
+
+          // measure digest performance
+          var rootDigestStart = window.performance.now();
+          for (var i = 0; i < 30; i++) {
+            $rootScope.$apply();
+          }
+          console.log("Dashboard::Performance Root Digest " + ((window.performance.now() - rootDigestStart) / 30));
+
+        }, 3000);
+
+      });
+
+    };
+
+    $scope.init();
+  }
+}
+
+export function grafanaAppDirective() {
+  return {
+    restrict: 'E',
+    controller: GrafanaCtrl,
+    link: (scope, elem) => {
+      var ignoreSideMenuHide;
+      // handle sidemenu open state
+      scope.$watch('contextSrv.sidemenu', newVal => {
+        if (newVal !== undefined) {
+          elem.toggleClass('sidemenu-open', scope.contextSrv.sidemenu);
+        }
+        if (scope.contextSrv.sidemenu) {
+          ignoreSideMenuHide = true;
+          setTimeout(() => {
+            ignoreSideMenuHide = false;
+          }, 300);
+        }
+      });
+
+      // handle document clicks that should hide things
+      elem.click(function(evt) {
+        if ($(evt.target).parents().length === 0) {
+          return;
+        }
+
+        // hide search
+        if (elem.find('.search-container').length > 0) {
+          if ($(evt.target).parents('.search-container').length === 0) {
+            scope.appEvent('hide-dash-search');
+          }
+        }
+        // hide sidemenu
+        if (!ignoreSideMenuHide &&  elem.find('.sidemenu').length > 0) {
+          if ($(evt.target).parents('.sidemenu').length === 0) {
+            scope.$apply(() => scope.contextSrv.toggleSideMenu());
+          }
+        }
+      });
+    }
+  };
+}
+
+coreModule.directive('grafanaApp', grafanaAppDirective);

+ 0 - 2
public/app/core/controllers/all.js

@@ -1,5 +1,4 @@
 define([
-  './grafana_ctrl',
   './search_ctrl',
   './inspect_ctrl',
   './json_editor_ctrl',
@@ -7,6 +6,5 @@ define([
   './invited_ctrl',
   './signup_ctrl',
   './reset_password_ctrl',
-  './sidemenu_ctrl',
   './error_ctrl',
 ], function () {});

+ 0 - 140
public/app/core/controllers/grafana_ctrl.ts

@@ -1,140 +0,0 @@
-///<reference path="../../headers/common.d.ts" />
-
-import config from 'app/core/config';
-import store from 'app/core/store';
-import _ from 'lodash';
-import angular from 'angular';
-import $ from 'jquery';
-import coreModule from '../core_module';
-
-coreModule.controller('GrafanaCtrl', function($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv) {
-
-  $scope.init = function() {
-    $scope.contextSrv = contextSrv;
-
-    $scope._ = _;
-
-    $rootScope.profilingEnabled = store.getBool('profilingEnabled');
-    $rootScope.performance = { loadStart: new Date().getTime() };
-    $rootScope.appSubUrl = config.appSubUrl;
-
-    if ($rootScope.profilingEnabled) { $scope.initProfiling(); }
-
-    alertSrv.init();
-    utilSrv.init();
-
-    $scope.dashAlerts = alertSrv;
-  };
-
-  $scope.initDashboard = function(dashboardData, viewScope) {
-    $controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
-  };
-
-  $rootScope.onAppEvent = function(name, callback, localScope) {
-    var unbind = $rootScope.$on(name, callback);
-    var callerScope = this;
-    if (callerScope.$id === 1 && !localScope) {
-      console.log('warning rootScope onAppEvent called without localscope');
-    }
-    if (localScope) {
-      callerScope = localScope;
-    }
-    callerScope.$on('$destroy', unbind);
-  };
-
-  $rootScope.appEvent = function(name, payload) {
-    $rootScope.$emit(name, payload);
-  };
-
-  $rootScope.colors = [
-    "#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0",
-    "#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477",
-    "#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0",
-    "#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93",
-    "#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7",
-    "#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B",
-    "#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7"
-  ];
-
-  $scope.getTotalWatcherCount = function() {
-    var count = 0;
-    var scopes = 0;
-    var root = $(document.getElementsByTagName('body'));
-
-    var f = function (element) {
-      if (element.data().hasOwnProperty('$scope')) {
-        scopes++;
-        angular.forEach(element.data().$scope.$$watchers, function () {
-          count++;
-        });
-      }
-
-      angular.forEach(element.children(), function (childElement) {
-        f($(childElement));
-      });
-    };
-
-    f(root);
-    $rootScope.performance.scopeCount = scopes;
-    return count;
-  };
-
-  $scope.initProfiling = function() {
-    var count = 0;
-
-    $scope.$watch(function digestCounter() {
-      count++;
-    }, function() {
-      // something
-    });
-
-    $rootScope.performance.panels = [];
-
-    $scope.$on('refresh', function() {
-      if ($rootScope.performance.panels.length > 0) {
-        var totalRender = 0;
-        var totalQuery = 0;
-
-        _.each($rootScope.performance.panels, function(panelTiming: any) {
-          totalRender += panelTiming.render;
-          totalQuery += panelTiming.query;
-        });
-
-        console.log('total query: ' + totalQuery);
-        console.log('total render: ' + totalRender);
-        console.log('avg render: ' + totalRender / $rootScope.performance.panels.length);
-      }
-
-      $rootScope.performance.panels = [];
-    });
-
-    $scope.onAppEvent('dashboard-loaded', function() {
-      count = 0;
-
-      setTimeout(function() {
-        console.log("Dashboard::Performance Total Digests: " + count);
-        console.log("Dashboard::Performance Total Watchers: " + $scope.getTotalWatcherCount());
-        console.log("Dashboard::Performance Total ScopeCount: " + $rootScope.performance.scopeCount);
-
-        var timeTaken = $rootScope.performance.allPanelsInitialized - $rootScope.performance.dashboardLoadStart;
-        console.log("Dashboard::Performance - All panels initialized in " + timeTaken + " ms");
-
-        // measure digest performance
-        var rootDigestStart = window.performance.now();
-        for (var i = 0; i < 30; i++) {
-          $rootScope.$apply();
-        }
-        console.log("Dashboard::Performance Root Digest " + ((window.performance.now() - rootDigestStart) / 30));
-
-      }, 3000);
-
-    });
-
-  };
-
-  $scope.init();
-
-});
-
-var grafanaCtrl = {};
-export default grafanaCtrl;

+ 0 - 130
public/app/core/controllers/sidemenu_ctrl.js

@@ -1,130 +0,0 @@
-define([
-  'angular',
-  'lodash',
-  'jquery',
-  '../core_module',
-  'app/core/config',
-],
-function (angular, _, $, coreModule, config) {
-  'use strict';
-
-  coreModule.default.controller('SideMenuCtrl', function($scope, $location, contextSrv, backendSrv) {
-
-    $scope.getUrl = function(url) {
-      return config.appSubUrl + url;
-    };
-
-    $scope.setupMainNav = function() {
-      _.each(config.bootData.mainNavLinks, function(item) {
-        $scope.mainLinks.push({
-          text: item.text,
-          icon: item.icon,
-          img: item.img,
-          url: $scope.getUrl(item.url)
-        });
-      });
-    };
-
-    $scope.openUserDropdown = function() {
-      $scope.orgMenu = [
-        {section: 'You', cssClass: 'dropdown-menu-title'},
-        {text: 'Profile', url: $scope.getUrl('/profile')},
-      ];
-
-      if (contextSrv.hasRole('Admin')) {
-        $scope.orgMenu.push({section: contextSrv.user.orgName, cssClass: 'dropdown-menu-title'});
-        $scope.orgMenu.push({
-          text: "Settings",
-          url: $scope.getUrl("/org"),
-        });
-        $scope.orgMenu.push({
-          text: "Users",
-          url: $scope.getUrl("/org/users"),
-        });
-        $scope.orgMenu.push({
-          text: "API Keys",
-          url: $scope.getUrl("/org/apikeys"),
-        });
-      }
-
-      $scope.orgMenu.push({cssClass: "divider"});
-
-      if (config.allowOrgCreate) {
-        $scope.orgMenu.push({text: "New organization", icon: "fa fa-fw fa-plus", url: $scope.getUrl('/org/new')});
-      }
-
-      backendSrv.get('/api/user/orgs').then(function(orgs) {
-        _.each(orgs, function(org) {
-          if (org.orgId === contextSrv.user.orgId) {
-            return;
-          }
-
-          $scope.orgMenu.push({
-            text: "Switch to " + org.name,
-            icon: "fa fa-fw fa-random",
-            click: function() {
-              $scope.switchOrg(org.orgId);
-            }
-          });
-        });
-
-        $scope.orgMenu.push({cssClass: "divider"});
-        if (contextSrv.isGrafanaAdmin) {
-          $scope.orgMenu.push({text: "Server admin", url: $scope.getUrl("/admin/settings")});
-        }
-        if (contextSrv.isSignedIn) {
-          $scope.orgMenu.push({text: "Sign out", url: $scope.getUrl("/logout"), target: "_self"});
-        }
-      });
-    };
-
-    $scope.switchOrg = function(orgId) {
-      backendSrv.post('/api/user/using/' + orgId).then(function() {
-        window.location.href = $scope.getUrl('/');
-      });
-    };
-
-    $scope.setupAdminNav = function() {
-      $scope.systemSection = true;
-      $scope.grafanaVersion = config.buildInfo.version;
-
-      $scope.mainLinks.push({
-        text: "System info",
-        icon: "fa fa-fw fa-info",
-        href: $scope.getUrl("/admin/settings"),
-      });
-
-      $scope.mainLinks.push({
-        text: "Global Users",
-        icon: "fa fa-fw fa-user",
-        href: $scope.getUrl("/admin/users"),
-      });
-
-      $scope.mainLinks.push({
-        text: "Global Orgs",
-        icon: "fa fa-fw fa-users",
-        href: $scope.getUrl("/admin/orgs"),
-      });
-    };
-
-    $scope.updateMenu = function() {
-      $scope.systemSection = false;
-      $scope.mainLinks = [];
-      $scope.orgMenu = [];
-
-      var currentPath = $location.path();
-      if (currentPath.indexOf('/admin') === 0) {
-        $scope.setupAdminNav();
-      } else {
-        $scope.setupMainNav();
-      }
-    };
-
-    $scope.init = function() {
-      $scope.showSignout = contextSrv.isSignedIn && !config['authProxyEnabled'];
-      $scope.updateMenu();
-      $scope.$on('$routeChangeSuccess', $scope.updateMenu);
-    };
-  });
-
-});

+ 2 - 1
public/app/core/core.ts

@@ -21,6 +21,7 @@ import "./directives/give_focus";
 import './jquery_extended';
 import './partials';
 
+import {grafanaAppDirective} from './components/grafana_app';
 import {arrayJoin} from './directives/array_join';
 import 'app/core/controllers/all';
 import 'app/core/services/all';
@@ -28,4 +29,4 @@ import 'app/core/routes/all';
 import './filters/filters';
 import coreModule from './core_module';
 
-export {arrayJoin, coreModule};
+export {arrayJoin, coreModule, grafanaAppDirective};

+ 1 - 5
public/app/core/directives/topnav.js

@@ -18,7 +18,7 @@ function (coreModule) {
         '<div class="navbar navbar-static-top"><div class="navbar-inner"><div class="container-fluid">' +
         '<div class="top-nav">' +
 				'<div class="top-nav-btn top-nav-menu-btn">' +
-					'<a class="pointer" ng-click="toggle()">' +
+					'<a class="pointer" ng-click="contextSrv.toggleSideMenu()">' +
 						'<span class="top-nav-logo-background">' +
 							'<img class="logo-icon" src="img/fav32.png"></img>' +
 						'</span>' +
@@ -43,10 +43,6 @@ function (coreModule) {
       link: function(scope, elem, attrs) {
         scope.icon = attrs.icon;
         scope.contextSrv = contextSrv;
-
-        scope.toggle = function() {
-          contextSrv.toggleSideMenu();
-        };
       }
     };
   });

+ 1 - 5
public/app/core/services/context_srv.js

@@ -20,12 +20,8 @@ function (angular, _, coreModule, store, config) {
       return this.user.orgRole === role;
     };
 
-    this.setSideMenuState = function(state) {
-      this.sidemenu = state;
-    };
-
     this.toggleSideMenu = function() {
-      this.setSideMenuState(!this.sidemenu);
+      this.sidemenu = !this.sidemenu;
     };
 
     this.version = config.buildInfo.version;

+ 7 - 22
public/app/features/dashboard/directives/dashSearchView.js

@@ -7,29 +7,12 @@ function (angular, $) {
 
   angular
     .module('grafana.directives')
-    .directive('dashSearchView', function($compile, $timeout) {
+    .directive('dashSearchView', function($compile) {
       return {
         restrict: 'A',
         link: function(scope, elem) {
           var editorScope;
-
-          function hookUpHideWhenClickedOutside() {
-            $timeout(function() {
-              $(document).bind('click.hide-search', function(evt) {
-                // some items can be inside container
-                // but then removed
-                if ($(evt.target).parents().length === 0) {
-                  return;
-                }
-
-                if ($(evt.target).parents('.search-container').length === 0) {
-                  if (editorScope) {
-                    editorScope.dismiss();
-                  }
-                }
-              });
-            });
-          }
+          var ignoreHide;
 
           function showSearch() {
             if (editorScope) {
@@ -37,13 +20,13 @@ function (angular, $) {
               return;
             }
 
+            ignoreHide = true;
             editorScope = scope.$new();
             editorScope.dismiss = function() {
               editorScope.$destroy();
               elem.empty();
               elem.unbind();
               editorScope = null;
-              $(document).unbind('click.hide-search');
             };
 
             var view = $('<div class="search-container" ng-include="\'app/partials/search.html\'"></div>');
@@ -51,11 +34,13 @@ function (angular, $) {
             elem.append(view);
             $compile(elem.contents())(editorScope);
 
-            hookUpHideWhenClickedOutside();
+            setTimeout(function() {
+              ignoreHide = false;
+            }, 300);
           }
 
           function hideSearch() {
-            if (editorScope) {
+            if (editorScope && !ignoreHide) {
               editorScope.dismiss();
             }
           }

+ 3 - 6
public/views/index.html

@@ -25,9 +25,8 @@
 
 	</head>
 
-	<body ng-cloak ng-controller="GrafanaCtrl" ng-class="{'sidemenu-open': contextSrv.sidemenu}">
-		<div class="sidemenu-canvas">
-
+	<body ng-cloak>
+		<grafana-app>
 			<aside class="sidemenu-wrapper">
 				<sidemenu ng-if="contextSrv.sidemenu"></sidemenu>
 			</aside>
@@ -43,9 +42,7 @@
 			</div>
 
 			<div ng-view class="main-view"></div>
-
-		</div>
-
+		</grafana-app>
   </body>
 
 	<script>