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

This commit is contained in:
Torkel Ödegaard 2016-01-16 18:55:13 +01:00
parent 317b5e6d86
commit c201f4c63e
9 changed files with 198 additions and 311 deletions

View File

@ -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);

View File

@ -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 () {});

View File

@ -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;

View File

@ -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);
};
});
});

View File

@ -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};

View File

@ -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();
};
}
};
});

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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>