diff --git a/public/app/features/dashboard/all.js b/public/app/features/dashboard/all.js
index 8d66c2eb1b2..edcc3c74d8e 100644
--- a/public/app/features/dashboard/all.js
+++ b/public/app/features/dashboard/all.js
@@ -22,5 +22,6 @@ define([
'./export/export_modal',
'./dash_list_ctrl',
'./ad_hoc_filters',
- './row/row',
+ './row/row_ctrl',
+ './repeat_option/repeat_option',
], function () {});
diff --git a/public/app/features/dashboard/dynamic_dashboard_srv.ts b/public/app/features/dashboard/dynamic_dashboard_srv.ts
index a7189627820..9f8fe7f7ab2 100644
--- a/public/app/features/dashboard/dynamic_dashboard_srv.ts
+++ b/public/app/features/dashboard/dynamic_dashboard_srv.ts
@@ -5,6 +5,7 @@ import angular from 'angular';
import _ from 'lodash';
import coreModule from 'app/core/core_module';
+import {DashboardRow} from './row/row_model';
export class DynamicDashboardSrv {
iteration: number;
@@ -45,7 +46,7 @@ export class DynamicDashboardSrv {
}
} else if (row.repeatRowId && row.repeatIteration !== this.iteration) {
// clean up old left overs
- this.dashboard.rows.splice(i, 1);
+ this.dashboard.removeRow(row, true);
i = i - 1;
continue;
}
@@ -80,12 +81,14 @@ export class DynamicDashboardSrv {
row = this.dashboard.rows[i];
if (row.repeatRowId === sourceRowId && row.repeatIteration !== this.iteration) {
copy = row;
+ copy.copyPropertiesFromRowSource(sourceRow);
break;
}
}
if (!copy) {
- copy = angular.copy(sourceRow);
+ var modelCopy = angular.copy(sourceRow.getSaveModel());
+ copy = new DashboardRow(modelCopy);
this.dashboard.rows.splice(sourceRowIndex + repeatIndex, 0, copy);
// set new panel ids
diff --git a/public/app/features/dashboard/model.ts b/public/app/features/dashboard/model.ts
index 3a544e9672f..167ce20ea2b 100644
--- a/public/app/features/dashboard/model.ts
+++ b/public/app/features/dashboard/model.ts
@@ -6,7 +6,7 @@ import moment from 'moment';
import _ from 'lodash';
import $ from 'jquery';
-import {Emitter, contextSrv} from 'app/core/core';
+import {Emitter, contextSrv, appEvents} from 'app/core/core';
import {DashboardRow} from './row/row_model';
export class DashboardModel {
@@ -169,6 +169,27 @@ export class DashboardModel {
row.addPanel(panel);
}
+ removeRow(row, force?) {
+ var index = _.indexOf(this.rows, row);
+
+ if (!row.panels.length || force) {
+ this.rows.splice(index, 1);
+ row.destroy();
+ return;
+ }
+
+ appEvents.emit('confirm-modal', {
+ title: 'Delete',
+ text: 'Are you sure you want to delete this row?',
+ icon: 'fa-trash',
+ yesText: 'Delete',
+ onConfirm: () => {
+ this.rows.splice(index, 1);
+ row.destroy();
+ }
+ });
+ }
+
toggleEditMode() {
this.editMode = !this.editMode;
this.updateSubmenuVisibility();
@@ -234,7 +255,7 @@ export class DashboardModel {
destroy() {
this.events.removeAllListeners();
for (let row of this.rows) {
- row.events.removeAllListeners();
+ row.destroy();
}
}
diff --git a/public/app/features/dashboard/repeat_option/repeat_option.ts b/public/app/features/dashboard/repeat_option/repeat_option.ts
new file mode 100644
index 00000000000..380c2861a28
--- /dev/null
+++ b/public/app/features/dashboard/repeat_option/repeat_option.ts
@@ -0,0 +1,34 @@
+///
+
+import {coreModule} from 'app/core/core';
+
+var template = `
+
+
+`;
+
+coreModule.directive('dashRepeatOption', function(variableSrv) {
+ return {
+ restrict: 'E',
+ template: template,
+ scope: {
+ model: "=",
+ },
+ link: function(scope, element) {
+ element.css({display: 'block', width: '100%'});
+
+ scope.variables = variableSrv.variables.map(item => {
+ return {text: item.name, value: item.name};
+ });
+
+ if (scope.variables.length === 0) {
+ scope.variables.unshift({text: 'No template variables found', value: null});
+ }
+
+ scope.variables.unshift({text: 'Disabled', value: null});
+ }
+ };
+});
+
diff --git a/public/app/features/dashboard/row/options.html b/public/app/features/dashboard/row/options.html
index c9227324001..96655b4970c 100644
--- a/public/app/features/dashboard/row/options.html
+++ b/public/app/features/dashboard/row/options.html
@@ -1,6 +1,5 @@
diff --git a/public/app/features/dashboard/row/row.ts b/public/app/features/dashboard/row/row.ts
deleted file mode 100644
index ad05865411e..00000000000
--- a/public/app/features/dashboard/row/row.ts
+++ /dev/null
@@ -1,267 +0,0 @@
-///
-
-import _ from 'lodash';
-import $ from 'jquery';
-import angular from 'angular';
-
-import config from 'app/core/config';
-import {coreModule} from 'app/core/core';
-
-import './options';
-import './add_panel';
-
-export class DashRowCtrl {
- dashboard: any;
- row: any;
- dropView: number;
-
- /** @ngInject */
- constructor(private $scope, private $rootScope, private $timeout, private uiSegmentSrv, private $q) {
- this.row.title = this.row.title || 'Row title';
-
- if (this.row.isNew) {
- this.dropView = 1;
- delete this.row.isNew;
- }
- }
-
- onDrop(panelId, dropTarget) {
- var dragObject;
-
- // if string it's a panel type
- if (_.isString(panelId)) {
- // setup new panel
- dragObject = {
- row: this.row,
- panel: {
- title: config.new_panel_title,
- type: panelId,
- id: this.dashboard.getNextPanelId(),
- },
- isNew: true,
- };
- } else {
- dragObject = this.dashboard.getPanelInfoById(panelId);
- }
-
- if (dropTarget) {
- dropTarget = this.dashboard.getPanelInfoById(dropTarget.id);
- // if draging new panel onto existing panel split it
- if (dragObject.isNew) {
- dragObject.panel.span = dropTarget.panel.span = dropTarget.panel.span/2;
- // insert after
- dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
- } else if (this.row === dragObject.row) {
- // just move element
- this.row.movePanel(dropTarget.index, dragObject.index);
- } else {
- // split drop target space
- dragObject.panel.span = dropTarget.panel.span = dropTarget.panel.span/2;
- // insert after
- dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
- // remove from source row
- dragObject.row.removePanel(dragObject.panel);
- }
- // dropInfo.row.panels[dropInfo.index] = info.panel;
- // info.row.panels[info.index] = dropTarget;
- // var dragSpan = info.panel.span;
- // info.panel.span = dropTarget.span;
- // dropTarget.span = dragSpan;
- } else {
- dragObject.panel.span = 12 - this.row.span;
- this.row.panels.push(dragObject.panel);
-
- // if not new remove from source row
- if (!dragObject.isNew) {
- dragObject.row.removePanel(dragObject.panel);
- }
- }
-
- this.row.panelSpanChanged();
- this.$timeout(() => {
- this.$rootScope.$broadcast('render');
- });
- }
-
- setHeight(height) {
- this.row.height = height;
- this.$scope.$broadcast('render');
- }
-
- moveRow(direction) {
- var rowsList = this.dashboard.rows;
- var rowIndex = _.indexOf(rowsList, this.row);
- var newIndex = rowIndex;
- switch (direction) {
- case 'up': {
- newIndex = rowIndex - 1;
- break;
- }
- case 'down': {
- newIndex = rowIndex + 1;
- break;
- }
- case 'top': {
- newIndex = 0;
- break;
- }
- case 'bottom': {
- newIndex = rowsList.length - 1;
- break;
- }
- default: {
- newIndex = rowIndex;
- }
- }
- if (newIndex >= 0 && newIndex <= (rowsList.length - 1)) {
- _.move(rowsList, rowIndex, newIndex);
- }
- }
-
- toggleCollapse() {
- this.dropView = 0;
- this.row.collapse = !this.row.collapse;
- }
-
- showAddPanel() {
- this.row.collapse = false;
- this.dropView = this.dropView === 1 ? 0 : 1;
- }
-
- showRowOptions() {
- this.dropView = this.dropView === 2 ? 0 : 2;
- }
-}
-
-export function rowDirective($rootScope) {
- return {
- restrict: 'E',
- templateUrl: 'public/app/features/dashboard/row/row.html',
- controller: DashRowCtrl,
- bindToController: true,
- controllerAs: 'ctrl',
- scope: {
- dashboard: "=",
- row: "=",
- },
- link: function(scope, element) {
- scope.$watchGroup(['ctrl.row.collapse', 'ctrl.row.height'], function() {
- element.find('.panels-wrapper').css({minHeight: scope.ctrl.row.collapse ? '5px' : scope.ctrl.row.height});
- });
-
- $rootScope.onAppEvent('panel-fullscreen-enter', function(evt, info) {
- var hasPanel = _.find(scope.ctrl.row.panels, {id: info.panelId});
- if (!hasPanel) {
- element.hide();
- }
- }, scope);
-
- $rootScope.onAppEvent('panel-fullscreen-exit', function() {
- element.show();
- }, scope);
- }
- };
-}
-
-coreModule.directive('dashRow', rowDirective);
-
-
-coreModule.directive('panelWidth', function($rootScope) {
- return function(scope, element) {
- var fullscreen = false;
-
- function updateWidth() {
- if (!fullscreen) {
- element[0].style.width = ((scope.panel.span / 1.2) * 10) + '%';
- }
- }
-
- $rootScope.onAppEvent('panel-fullscreen-enter', function(evt, info) {
- fullscreen = true;
-
- if (scope.panel.id !== info.panelId) {
- element.hide();
- } else {
- element[0].style.width = '100%';
- }
- }, scope);
-
- $rootScope.onAppEvent('panel-fullscreen-exit', function(evt, info) {
- fullscreen = false;
-
- if (scope.panel.id !== info.panelId) {
- element.show();
- }
-
- updateWidth();
- }, scope);
-
- scope.$watch('panel.span', updateWidth);
-
- if (fullscreen) {
- element.hide();
- }
- };
-});
-
-
-coreModule.directive('panelDropZone', function($timeout) {
- return function(scope, element) {
- var row = scope.ctrl.row;
- var indrag = false;
- var textEl = element.find('.panel-drop-zone-text');
-
- function showPanel(span, text) {
- element.find('.panel-container').css('height', row.height);
- element[0].style.width = ((span / 1.2) * 10) + '%';
- textEl.text(text);
- element.show();
- }
-
- function hidePanel() {
- element.hide();
- // element.removeClass('panel-drop-zone--empty');
- }
-
- function updateState() {
- if (scope.ctrl.dashboard.editMode) {
- if (row.panels.length === 0 && indrag === false) {
- return showPanel(12, 'Empty Space');
- }
-
- var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
- if (dropZoneSpan > 0) {
- if (indrag) {
- return showPanel(dropZoneSpan, 'Drop Here');
- } else {
- return showPanel(dropZoneSpan, 'Empty Space');
- }
- }
- }
-
- if (indrag === true) {
- var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
- if (dropZoneSpan > 1) {
- return showPanel(dropZoneSpan, 'Drop Here');
- }
- }
-
- hidePanel();
- }
-
- row.events.on('span-changed', updateState, scope);
-
- scope.$watchGroup(['ctrl.dashboard.editMode'], updateState);
-
- scope.$on("ANGULAR_DRAG_START", function() {
- indrag = true;
- updateState();
- });
-
- scope.$on("ANGULAR_DRAG_END", function() {
- indrag = false;
- updateState();
- });
- };
-});
-
diff --git a/public/app/features/dashboard/row/row_ctrl.ts b/public/app/features/dashboard/row/row_ctrl.ts
index 2f5b1446a17..dad66ceb024 100644
--- a/public/app/features/dashboard/row/row_ctrl.ts
+++ b/public/app/features/dashboard/row/row_ctrl.ts
@@ -1,11 +1,9 @@
///
import _ from 'lodash';
-import $ from 'jquery';
-import angular from 'angular';
import config from 'app/core/config';
-import {coreModule} from 'app/core/core';
+import {coreModule, appEvents} from 'app/core/core';
import './options';
import './add_panel';
@@ -19,28 +17,63 @@ export class DashRowCtrl {
constructor(private $scope, private $rootScope, private $timeout, private uiSegmentSrv, private $q) {
this.row.title = this.row.title || 'Row title';
- if (this.dashboard.meta.isNew) {
+ if (this.row.isNew) {
this.dropView = 1;
delete this.row.isNew;
}
}
onDrop(panelId, dropTarget) {
- var info = this.dashboard.getPanelInfoById(panelId);
- if (dropTarget) {
- var dropInfo = this.dashboard.getPanelInfoById(dropTarget.id);
- dropInfo.row.panels[dropInfo.index] = info.panel;
- info.row.panels[info.index] = dropTarget;
- var dragSpan = info.panel.span;
- info.panel.span = dropTarget.span;
- dropTarget.span = dragSpan;
+ var dragObject;
+
+ // if string it's a panel type
+ if (_.isString(panelId)) {
+ // setup new panel
+ dragObject = {
+ row: this.row,
+ panel: {
+ title: config.new_panel_title,
+ type: panelId,
+ id: this.dashboard.getNextPanelId(),
+ },
+ isNew: true,
+ };
} else {
- info.row.panels.splice(info.index, 1);
- info.panel.span = 12 - this.dashboard.rowSpan(this.row);
- this.row.panels.push(info.panel);
+ dragObject = this.dashboard.getPanelInfoById(panelId);
}
- this.$rootScope.$broadcast('render');
+ if (dropTarget) {
+ dropTarget = this.dashboard.getPanelInfoById(dropTarget.id);
+ // if draging new panel onto existing panel split it
+ if (dragObject.isNew) {
+ dragObject.panel.span = dropTarget.panel.span = dropTarget.panel.span/2;
+ // insert after
+ dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
+ } else if (this.row === dragObject.row) {
+ // just move element
+ this.row.movePanel(dropTarget.index, dragObject.index);
+ } else {
+ // split drop target space
+ dragObject.panel.span = dropTarget.panel.span = dropTarget.panel.span/2;
+ // insert after
+ dropTarget.row.panels.splice(dropTarget.index+1, 0, dragObject.panel);
+ // remove from source row
+ dragObject.row.removePanel(dragObject.panel);
+ }
+ } else {
+ dragObject.panel.span = 12 - this.row.span;
+ this.row.panels.push(dragObject.panel);
+
+ // if not new remove from source row
+ if (!dragObject.isNew) {
+ dragObject.row.removePanel(dragObject.panel);
+ }
+ }
+
+ this.row.panelSpanChanged();
+ this.$timeout(() => {
+ this.$rootScope.$broadcast('render');
+ });
}
setHeight(height) {
@@ -51,28 +84,8 @@ export class DashRowCtrl {
moveRow(direction) {
var rowsList = this.dashboard.rows;
var rowIndex = _.indexOf(rowsList, this.row);
- var newIndex = rowIndex;
- switch (direction) {
- case 'up': {
- newIndex = rowIndex - 1;
- break;
- }
- case 'down': {
- newIndex = rowIndex + 1;
- break;
- }
- case 'top': {
- newIndex = 0;
- break;
- }
- case 'bottom': {
- newIndex = rowsList.length - 1;
- break;
- }
- default: {
- newIndex = rowIndex;
- }
- }
+ var newIndex = rowIndex + direction;
+
if (newIndex >= 0 && newIndex <= (rowsList.length - 1)) {
_.move(rowsList, rowIndex, newIndex);
}
@@ -93,7 +106,7 @@ export class DashRowCtrl {
}
}
-export function rowDirective($rootScope) {
+coreModule.directive('dashRow', function($rootScope) {
return {
restrict: 'E',
templateUrl: 'public/app/features/dashboard/row/row.html',
@@ -121,10 +134,7 @@ export function rowDirective($rootScope) {
}, scope);
}
};
-}
-
-coreModule.directive('dashRow', rowDirective);
-
+});
coreModule.directive('panelWidth', function($rootScope) {
return function(scope, element) {
@@ -180,7 +190,6 @@ coreModule.directive('panelDropZone', function($timeout) {
function hidePanel() {
element.hide();
- // element.removeClass('panel-drop-zone--empty');
}
function updateState() {
@@ -190,7 +199,7 @@ coreModule.directive('panelDropZone', function($timeout) {
}
var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
- if (dropZoneSpan > 1) {
+ if (dropZoneSpan > 0) {
if (indrag) {
return showPanel(dropZoneSpan, 'Drop Here');
} else {
@@ -200,26 +209,22 @@ coreModule.directive('panelDropZone', function($timeout) {
}
if (indrag === true) {
- return showPanel(dropZoneSpan, 'Drop Here');
+ var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
+ if (dropZoneSpan > 1) {
+ return showPanel(dropZoneSpan, 'Drop Here');
+ }
}
hidePanel();
}
- scope.row.events.on('panel-added', updateState);
- scope.row.events.on('span-changed', updateState);
+ row.events.on('span-changed', updateState, scope);
- scope.$watchGroup(['ctrl.row.panels.length', 'ctrl.dashboard.editMode', 'ctrl.row.span'], updateState);
+ scope.$watchGroup(['ctrl.dashboard.editMode'], updateState);
scope.$on("ANGULAR_DRAG_START", function() {
indrag = true;
updateState();
- // $timeout(function() {
- // var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
- // if (dropZoneSpan > 0) {
- // showPanel(dropZoneSpan, 'Panel Drop Zone');
- // }
- // });
});
scope.$on("ANGULAR_DRAG_END", function() {
diff --git a/public/app/features/dashboard/row/row_model.ts b/public/app/features/dashboard/row/row_model.ts
index d32a0c25259..67a0159d9cf 100644
--- a/public/app/features/dashboard/row/row_model.ts
+++ b/public/app/features/dashboard/row/row_model.ts
@@ -11,6 +11,7 @@ export class DashboardRow {
titleSize: any;
events: Emitter;
span: number;
+ height: number;
defaults = {
title: 'Dashboard Row',
@@ -19,6 +20,9 @@ export class DashboardRow {
titleSize: 'h6',
height: 250,
isNew: false,
+ repeat: null,
+ repeatRowId: null,
+ repeatIteration: null,
};
constructor(private model) {
@@ -86,5 +90,16 @@ export class DashboardRow {
movePanel(fromIndex, toIndex) {
this.panels.splice(toIndex, 0, this.panels.splice(fromIndex, 1)[0]);
}
+
+ destroy() {
+ this.events.removeAllListeners();
+ }
+
+ copyPropertiesFromRowSource(source) {
+ this.height = source.height;
+ this.title = source.title;
+ this.showTitle = source.showTitle;
+ this.titleSize = source.titleSize;
+ }
}
diff --git a/public/app/features/dashboard/rowCtrl.js b/public/app/features/dashboard/rowCtrl.js
deleted file mode 100644
index e5f063d5f63..00000000000
--- a/public/app/features/dashboard/rowCtrl.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// define([
-// 'angular',
-// 'lodash',
-// 'app/core/config'
-// ],
-// function (angular, _, config) {
-// 'use strict';
-//
-// var module = angular.module('grafana.controllers');
-//
-// module.controller('RowCtrl', function($scope, $rootScope, $timeout) {
-//
-// $scope.moveRow = function(direction) {
-// var rowsList = $scope.dashboard.rows;
-// var rowIndex = _.indexOf(rowsList, $scope.row);
-// var newIndex = rowIndex;
-// switch(direction) {
-// case 'up': {
-// newIndex = rowIndex - 1;
-// break;
-// }
-// case 'down': {
-// newIndex = rowIndex + 1;
-// break;
-// }
-// case 'top': {
-// newIndex = 0;
-// break;
-// }
-// case 'bottom': {
-// newIndex = rowsList.length - 1;
-// break;
-// }
-// default: {
-// newIndex = rowIndex;
-// }
-// }
-// if (newIndex >= 0 && newIndex <= (rowsList.length - 1)) {
-// _.move(rowsList, rowIndex, newIndex);
-// }
-// };
-//
-// $scope.setHeight = function(height) {
-// $scope.row.height = height;
-// $scope.$broadcast('render');
-// };
-//
-// $scope.init();
-// });
-//
-// });
diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts
index b19ff972368..de9f5c7ea9b 100644
--- a/public/app/features/panel/panel_directive.ts
+++ b/public/app/features/panel/panel_directive.ts
@@ -139,7 +139,7 @@ module.directive('panelResizer', function($rootScope) {
}
function moveHandler(e) {
- ctrl.row.height = originalHeight + (e.pageY - handleOffset.top);
+ ctrl.row.height = Math.round(originalHeight + (e.pageY - handleOffset.top));
ctrl.panel.span = originalWidth + (((e.pageX - handleOffset.left) / maxWidth) * 12);
ctrl.panel.span = Math.min(Math.max(ctrl.panel.span, 1), 12);
diff --git a/public/app/partials/panelgeneral.html b/public/app/partials/panelgeneral.html
index fd7fa19d082..99e56cc27de 100644
--- a/public/app/partials/panelgeneral.html
+++ b/public/app/partials/panelgeneral.html
@@ -18,9 +18,7 @@