Big refactoring of the way panels are loaded and how they are built using directives, this way feel cleaner and allowed for placing the panel edit box outside the panel-container

This commit is contained in:
Torkel Ödegaard 2015-02-02 21:59:04 +01:00
parent b67f4dc390
commit 04ec222462
26 changed files with 167 additions and 204 deletions

View File

@ -6,6 +6,8 @@ function () {
function PanelMeta(options) {
this.description = options.description;
this.fullscreen = options.fullscreen;
this.editIcon = options.editIcon;
this.panelName = options.panelName;
this.menu = [];
this.editorTabs = [];
this.extendedMenu = [];

View File

@ -18,7 +18,7 @@ function (_, crypto) {
panels : {
'graph': { path: 'panels/graph' },
'singlestat': { path: 'panels/singlestat' },
'text': { path: 'panels/text' }
'text': { path: 'panels/text' },
},
plugins : {},
default_route : '/dashboard/file/default.json',

View File

@ -143,7 +143,6 @@ function (angular, _, config, $) {
};
$scope.newDashboard = function() {
//$location.path('/dashboard/file/empty.json');
$location.url('dashboard/new');
};

View File

@ -1,7 +1,6 @@
define([
'./arrayJoin',
'./dashUpload',
'./grafanaPanel',
'./grafanaSimplePanel',
'./ngBlur',
'./dashEditLink',

View File

@ -1,80 +0,0 @@
define([
'angular',
'jquery',
'config',
'./panelMenu',
],
function (angular, $, config) {
'use strict';
angular
.module('grafana.directives')
.directive('grafanaPanel', function($compile, $parse) {
var container = '<div class="panel-container"></div>';
var content = '<div class="panel-content"></div>';
var panelHeader =
'<div class="panel-header">'+
'<span class="alert-error panel-error small pointer"' +
'config-modal="app/partials/inspector.html" ng-if="panelMeta.error">' +
'<span data-placement="top" bs-tooltip="panelMeta.error">' +
'<i class="fa fa-exclamation"></i><span class="panel-error-arrow"></span>' +
'</span>' +
'</span>' +
'<span class="panel-loading" ng-show="panelMeta.loading">' +
'<i class="fa fa-spinner fa-spin"></i>' +
'</span>' +
'<div class="panel-title-container drag-handle" panel-menu></div>' +
'</div>'+
'</div>';
return {
restrict: 'E',
link: function($scope, elem, attr) {
var getter = $parse(attr.type), panelType = getter($scope);
var newScope = $scope.$new();
// compile the module and uncloack. We're done
function loadModule($module) {
$module.appendTo(elem);
elem.wrap(container);
$compile(elem.contents())(newScope);
elem.removeClass("ng-cloak");
var panelCtrlElem = $(elem.children()[0]);
var panelCtrlScope = panelCtrlElem.data().$scope;
panelCtrlScope.$watchGroup(['fullscreen', 'panel.height', 'row.height'], function() {
panelCtrlElem.css({ minHeight: panelCtrlScope.panel.height || panelCtrlScope.row.height });
panelCtrlElem.toggleClass('panel-fullscreen', panelCtrlScope.fullscreen ? true : false);
});
}
newScope.$on('$destroy',function() {
elem.unbind();
elem.remove();
});
elem.addClass('ng-cloak');
var panelPath = config.panels[panelType].path;
$scope.require([
'jquery',
'text!'+panelPath+'/module.html',
panelPath + "/module",
], function ($, moduleTemplate) {
var $module = $(moduleTemplate);
$module.prepend(panelHeader);
$module.first().find('.panel-header').nextAll().wrapAll(content);
loadModule($module);
});
}
};
});
});

View File

@ -1,18 +1,4 @@
<topnav toggle="toggleSideMenu()" icon="fa fa-shield" section="Account" show-menu-btn="!grafana.sidemenu">
<!-- <ul class="nav"> -->
<!-- <li> -->
<!-- <a href="asd">Details</a> -->
<!-- </li> -->
<!-- <li> -->
<!-- <a href="asd">Data Sources</a> -->
<!-- </li> -->
<!-- <li> -->
<!-- <a href="asd">Users</a> -->
<!-- </li> -->
<!-- <li> -->
<!-- <a href="asd">API Keys</a> -->
<!-- </li> -->
<!-- </ul> -->
</topnav>
<div class="gf-box" style="min-height: 500px">
@ -34,6 +20,10 @@
<div class="clearfix"></div>
</div>
</div>
<br>
<panel-loader type="'test'"></panel-loader>
<br>
<button type="submit" class="pull-right btn btn-success" ng-click="update()">Update</button>
</form>

View File

@ -7,6 +7,7 @@ define([
'./opentsdb/datasource',
'./elasticsearch/datasource',
'./dashboard/all',
'./panel/all',
'./profile/profileCtrl',
'./account/accountUsersCtrl',
'./account/datasourcesCtrl',

View File

@ -9,7 +9,6 @@ define([
'./keybindings',
'./viewStateSrv',
'./playlistSrv',
'./panelSrv',
'./soloPanelCtrl',
'./timeSrv',
'./unsavedChangesSrv',

View File

@ -118,7 +118,7 @@ function (angular, _, $) {
self.$scope.dashboard.emit_refresh();
}
else {
self.fullscreenPanel.$emit('render');
self.fullscreenPanel.$broadcast('render');
}
delete self.fullscreenPanel;
});
@ -139,7 +139,7 @@ function (angular, _, $) {
panelScope.fullscreen = true;
$timeout(function() {
panelScope.$emit('render');
panelScope.$broadcast('render');
});
};

View File

@ -0,0 +1,5 @@
define([
'./panelMenu',
'./panelDirective',
'./panelSrv',
], function () {});

View File

@ -0,0 +1,40 @@
define([
'angular',
'jquery',
'config',
],
function (angular, $, config) {
'use strict';
angular
.module('grafana.directives')
.directive('panelLoader', function($compile, $parse) {
return {
restrict: 'E',
link: function(scope, elem, attr) {
var getter = $parse(attr.type), panelType = getter(scope);
var panelPath = config.panels[panelType].path;
scope.require([panelPath + "/module"], function () {
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
elem.append(panelEl);
$compile(panelEl)(scope);
});
}
};
}).directive('grafanaPanel', function() {
return {
restrict: 'E',
templateUrl: '/app/features/panel/partials/panel.html',
transclude: true,
link: function(scope, elem) {
var panelContainer = elem.find('.panel-container');
scope.$watchGroup(['fullscreen', 'panel.height', 'row.height'], function() {
panelContainer.css({ minHeight: scope.panel.height || scope.row.height, display: 'block' });
elem.toggleClass('panel-fullscreen', scope.fullscreen ? true : false);
});
}
};
});
});

View File

@ -0,0 +1,42 @@
<div class="panel-container">
<div class="panel-header">
<span class="alert-error panel-error small pointer" config-modal="app/partials/inspector.html" ng-if="panelMeta.error">
<span data-placement="top" bs-tooltip="panelMeta.error">
<i class="fa fa-exclamation"></i><span class="panel-error-arrow"></span>
</span>
</span>
<span class="panel-loading" ng-show="panelMeta.loading">
<i class="fa fa-spinner fa-spin"></i>
</span>
<div class="panel-title-container drag-handle" panel-menu></div>
</div>
<div class="panel-content">
<ng-transclude></ng-transclude>
</div>
</div>
<div class="panel-full-edit" ng-if="editMode">
<div class="gf-box">
<div class="gf-box-header">
<div class="gf-box-title">
<i ng-class="panelMeta.editIcon"></i>
{{panelMeta.panelName}}
</div>
<div ng-model="editor.index" bs-tabs>
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
</div>
</div>
</div>
<div class="gf-box-body">
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
<div ng-include src="tab.src"></div>
</div>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
<div ng-controller='GraphCtrl'>
<grafana-panel>
<div class="graph-wrapper" ng-class="{'graph-legend-rightside': panel.legend.rightSide}">
<div class="graph-canvas-wrapper">
@ -24,23 +24,6 @@
<div class="clearfix"></div>
<div class="gf-box gf-box-full-edit" ng-if="editMode">
<div class="gf-box-header">
<div class="gf-box-title">
<i class="fa fa-bar-chart"></i>
Graph
</div>
</grafana-panel>
<div ng-model="editor.index" bs-tabs>
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
</div>
</div>
</div>
<div class="gf-box-body">
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
<div ng-include src="tab.src"></div>
</div>
</div>
</div>
</div>

View File

@ -16,10 +16,18 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
var module = angular.module('grafana.panels.graph');
module.directive('grafanaPanelGraph', function() {
return {
controller: 'GraphCtrl',
templateUrl: '/app/panels/graph/module.html',
};
});
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv) {
$scope.panelMeta = new PanelMeta({
description: 'Graph panel',
panelName: 'Graph',
editIcon: "fa fa-bar-chart",
fullscreen: true,
metricsEditor: true
});

View File

@ -1,26 +1,4 @@
<div ng-controller='SingleStatCtrl'>
<grafana-panel>
<div class="singlestat-panel" singlestat-panel></div>
<div class="clearfix"></div>
<div class="gf-box gf-box-full-edit" ng-if="editMode">
<div class="gf-box-header">
<div class="gf-box-title">
<i class="fa fa-dashboard"></i>
Singlestat
</div>
<div ng-model="editor.index" bs-tabs>
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
</div>
</div>
</div>
<div class="gf-box-body">
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
<div ng-include src="tab.src"></div>
</div>
</div>
</div>
</div>
</grafana-panel>

View File

@ -13,10 +13,18 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
var module = angular.module('grafana.panels.singlestat');
app.useModule(module);
module.directive('grafanaPanelSinglestat', function() {
return {
controller: 'SingleStatCtrl',
templateUrl: '/app/panels/singlestat/module.html',
};
});
module.controller('SingleStatCtrl', function($scope, panelSrv, timeSrv) {
$scope.panelMeta = new PanelMeta({
description: 'Singlestat panel',
panelName: 'Singlestat',
editIcon: "fa fa-dashboard",
fullscreen: true,
metricsEditor: true
});
@ -192,7 +200,7 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
data.colorMap = $scope.panel.colors;
$scope.data = data;
$scope.$emit('render');
$scope.$broadcast('render');
};
$scope.getFormatedValue = function(mainValue) {

View File

@ -1,4 +1,3 @@
<div ng-controller='text'>
<p ng-bind-html="content" ng-show="content">
</p>
</div>
<grafana-panel>
<p ng-bind-html="content" ng-show="content"></p>
</grafana-panel>

View File

@ -8,15 +8,24 @@ define([
function (angular, app, _, require, PanelMeta) {
'use strict';
var converter;
var module = angular.module('grafana.panels.text', []);
app.useModule(module);
var converter;
module.directive('grafanaPanelText', function() {
return {
controller: 'TextPanelCtrl',
templateUrl: '/app/panels/text/module.html',
};
});
module.controller('text', function($scope, templateSrv, $sce, panelSrv) {
module.controller('TextPanelCtrl', function($scope, templateSrv, $sce, panelSrv) {
$scope.panelMeta = new PanelMeta({
description : "A static text panel that can use plain text, markdown, or (sanitized) HTML"
panelName: 'Text',
editIcon: "fa fa-text-width",
fullscreen: true,
});
$scope.panelMeta.addEditorTab('Edit text', 'app/panels/text/editor.html');

View File

@ -79,12 +79,11 @@
</div>
<!-- Panels, draggable needs to be disabled in fullscreen because Firefox bug -->
<div ng-repeat="(name, panel) in row.panels"
class="panel"
<div ng-repeat="(name, panel) in row.panels" class="panel"
ui-draggable="{{!dashboardViewState.fullscreen}}" drag="panel.id"
ui-on-Drop="onDrop($data, row, panel)"
drag-handle-class="drag-handle" panel-width ng-model="panel">
<grafana-panel type="panel.type" ng-cloak></grafana-panel>
drag-handle-class="drag-handle" panel-width>
<panel-loader type="panel.type" class="panel-margin"></panel-loader>
</div>
<div panel-drop-zone class="panel panel-drop-zone"

View File

@ -1,22 +0,0 @@
<div bindonce class="gf-box-header">
<div class="gf-box-title">
<i class="fa fa-text-width"></i>
<span bo-text="panel.type+' settings'"></span>
</div>
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
</div>
</div>
<button class="gf-box-header-close-btn" ng-click="dismiss();">
<i class="fa fa-remove"></i>
</button>
</div>
<div class="gf-box-body">
<div ng-repeat="tab in panelMeta.editorTabs" ng-show="editor.index == $index">
<div ng-include src="tab.src"></div>
</div>
</div>

View File

@ -58,7 +58,7 @@ function (angular, store) {
$location.path('');
return;
}
$scope.initDashboard(window.grafanaImportDashboard, $scope);
$scope.initDashboard({ meta: {}, model: window.grafanaImportDashboard }, $scope);
});
module.controller('NewDashboardCtrl', function($scope) {

View File

@ -5,10 +5,6 @@
border: 1px solid @grafanaTargetFuncBackground;
}
.gf-box-full-edit {
margin: 30px 0 0 0;
}
.gf-box-no-margin {
margin: 0;
}

View File

@ -5,10 +5,13 @@
position: relative;
}
.panel-container {
padding: 0px 0px 0px 0px;
background: @grafanaPanelBackground;
.panel-margin {
margin: 5px;
display: block;
}
.panel-container {
background: @grafanaPanelBackground;
position: relative;
&:hover {
.panel-actions {
@ -91,17 +94,17 @@
top: 60px;
height: 100%;
padding: 0;
background: @grafanaPanelBackground;
overflow-y: auto;
height: 100%;
.panel-content {
padding-bottom: 130px;
}
.dropdown-menu {
margin-bottom: 70px;
}
.panel-container {
margin: 15px;
}
.panel-menu {
top: 0px;
}
@ -110,6 +113,11 @@
}
}
.panel-full-edit {
margin-top: 30px;
padding-bottom: 130px;
}
.panel-menu {
z-index: 1000;
position: absolute;

View File

@ -1,6 +1,6 @@
define([
'helpers',
'features/dashboard/panelSrv',
'features/panel/panelSrv',
'panels/graph/module'
], function(helpers) {
'use strict';