Prechádzať zdrojové kódy

Merge branch 'develop-settings' into develop

Torkel Ödegaard 8 rokov pred
rodič
commit
bbd0b98be9
52 zmenil súbory, kde vykonal 1128 pridanie a 1061 odobranie
  1. 2 2
      public/app/core/components/EmptyListCTA/EmptyListCTA.tsx
  2. 1 1
      public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.jest.tsx.snap
  3. 0 2
      public/app/core/components/dashboard_selector.ts
  4. 12 13
      public/app/core/directives/dash_class.js
  5. 0 1
      public/app/core/directives/dash_edit_link.js
  6. 10 8
      public/app/core/services/keybindingSrv.ts
  7. 7 0
      public/app/core/services/popover_srv.ts
  8. 1 1
      public/app/features/annotations/annotation_tooltip.ts
  9. 5 14
      public/app/features/annotations/editor_ctrl.ts
  10. 79 111
      public/app/features/annotations/partials/editor.html
  11. 2 1
      public/app/features/dashboard/all.ts
  12. 0 6
      public/app/features/dashboard/dashboard_ctrl.ts
  13. 0 2
      public/app/features/dashboard/dashboard_model.ts
  14. 13 18
      public/app/features/dashboard/dashnav/dashnav.html
  15. 17 70
      public/app/features/dashboard/dashnav/dashnav.ts
  16. 105 133
      public/app/features/dashboard/history/history.html
  17. 0 2
      public/app/features/dashboard/history/history.ts
  18. 0 65
      public/app/features/dashboard/partials/addAnnotationModal.html
  19. 0 95
      public/app/features/dashboard/partials/settings.html
  20. 0 2
      public/app/features/dashboard/save_as_modal.ts
  21. 109 0
      public/app/features/dashboard/settings/settings.html
  22. 156 0
      public/app/features/dashboard/settings/settings.ts
  23. 1 1
      public/app/features/dashboard/specs/save_as_modal.jest.ts
  24. 0 1
      public/app/features/dashboard/submenu/submenu.html
  25. 0 61
      public/app/features/dashboard/timepicker/dropdown.html
  26. 21 11
      public/app/features/dashboard/timepicker/settings.html
  27. 59 0
      public/app/features/dashboard/timepicker/timepicker.html
  28. 11 12
      public/app/features/dashboard/timepicker/timepicker.ts
  29. 57 60
      public/app/features/dashlinks/editor.html
  30. 11 4
      public/app/features/panel/panel_ctrl.ts
  31. 4 2
      public/app/features/templating/editor_ctrl.ts
  32. 242 265
      public/app/features/templating/partials/editor.html
  33. 0 1
      public/app/partials/confirm_modal.html
  34. 5 3
      public/app/partials/dashboard.html
  35. 0 2
      public/app/plugins/panel/graph/graph.ts
  36. 1 0
      public/sass/_grafana.scss
  37. 4 4
      public/sass/_variables.dark.scss
  38. 1 0
      public/sass/_variables.light.scss
  39. 1 1
      public/sass/_variables.scss
  40. 1 0
      public/sass/base/_grafana_icons.scss
  41. 80 0
      public/sass/components/_dashboard_settings.scss
  42. 15 12
      public/sass/components/_empty_list_cta.scss
  43. 9 3
      public/sass/components/_gf-form.scss
  44. 0 10
      public/sass/components/_infobox.scss
  45. 62 21
      public/sass/components/_navbar.scss
  46. 4 0
      public/sass/components/_query_editor.scss
  47. 1 1
      public/sass/components/_switch.scss
  48. 2 1
      public/sass/components/_tabbed_view.scss
  49. 6 0
      public/sass/components/_tabs.scss
  50. 8 7
      public/sass/components/_timepicker.scss
  51. 1 15
      public/sass/components/_view_states.scss
  52. 2 16
      public/sass/pages/_dashboard.scss

+ 2 - 2
public/app/core/components/EmptyListCTA/EmptyListCTA.tsx

@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React, { Component } from 'react';
 
 
 export interface IProps {
 export interface IProps {
     model: any;
     model: any;
@@ -17,7 +17,7 @@ class EmptyListCTA extends Component<IProps, any> {
             proTipTarget
             proTipTarget
         } = this.props.model;
         } = this.props.model;
         return (
         return (
-            <div className="empty-list-cta p-t-2 p-b-1">
+            <div className="empty-list-cta">
                 <div className="empty-list-cta__title">{title}</div>
                 <div className="empty-list-cta__title">{title}</div>
                 <a href={buttonLink} className="empty-list-cta__button btn btn-xlarge btn-success"><i className={buttonIcon} />{buttonTitle}</a>
                 <a href={buttonLink} className="empty-list-cta__button btn btn-xlarge btn-success"><i className={buttonIcon} />{buttonTitle}</a>
                 <div className="empty-list-cta__pro-tip">
                 <div className="empty-list-cta__pro-tip">

+ 1 - 1
public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.jest.tsx.snap

@@ -2,7 +2,7 @@
 
 
 exports[`CollorPalette renders correctly 1`] = `
 exports[`CollorPalette renders correctly 1`] = `
 <div
 <div
-  className="empty-list-cta p-t-2 p-b-1"
+  className="empty-list-cta"
 >
 >
   <div
   <div
     className="empty-list-cta__title"
     className="empty-list-cta__title"

+ 0 - 2
public/app/core/components/dashboard_selector.ts

@@ -1,5 +1,3 @@
-///<reference path="../../headers/common.d.ts" />
-
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 
 
 var template = `
 var template = `

+ 12 - 13
public/app/core/directives/dash_class.js

@@ -18,22 +18,21 @@ function (_, $, coreModule) {
           elem.toggleClass('panel-in-fullscreen', false);
           elem.toggleClass('panel-in-fullscreen', false);
         });
         });
 
 
-        var lastHideControlsVal;
-        $scope.$watch('ctrl.dashboard.hideControls', function() {
-          if (!$scope.dashboard) {
-            return;
-          }
-
-          var hideControls = $scope.dashboard.hideControls;
-          if (lastHideControlsVal !== hideControls) {
-            elem.toggleClass('hide-controls', hideControls);
-            lastHideControlsVal = hideControls;
-          }
-        });
-
         $scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) {
         $scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) {
           elem.toggleClass('playlist-active', newValue === true);
           elem.toggleClass('playlist-active', newValue === true);
         });
         });
+
+        $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) {
+          if (newValue) {
+            elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue));
+            setTimeout(function() {
+              elem.toggleClass('dashboard-page--settings-open', _.isString(newValue));
+            }, 10);
+          } else {
+            elem.removeClass('dashboard-page--settings-opening');
+            elem.removeClass('dashboard-page--settings-open');
+          }
+        });
       }
       }
     };
     };
   });
   });

+ 0 - 1
public/app/core/directives/dash_edit_link.js

@@ -13,7 +13,6 @@ function ($, angular, coreModule, _) {
     'templating':  { src: 'public/app/features/templating/partials/editor.html'},
     'templating':  { src: 'public/app/features/templating/partials/editor.html'},
     'history':     { html: '<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>'},
     'history':     { html: '<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>'},
     'timepicker':  { src: 'public/app/features/dashboard/timepicker/dropdown.html' },
     'timepicker':  { src: 'public/app/features/dashboard/timepicker/dropdown.html' },
-    'add-panel':    { html: '<add-panel></add-panel>' },
     'import':      { html: '<dash-import dismiss="dismiss()"></dash-import>', isModal: true },
     'import':      { html: '<dash-import dismiss="dismiss()"></dash-import>', isModal: true },
     'permissions': { html: '<dash-acl-modal dismiss="dismiss()"></dash-acl-modal>', isModal: true },
     'permissions': { html: '<dash-acl-modal dismiss="dismiss()"></dash-acl-modal>', isModal: true },
     'new-folder':  {
     'new-folder':  {

+ 10 - 8
public/app/core/services/keybindingSrv.ts

@@ -72,8 +72,8 @@ export class KeybindingSrv {
     }, 'keydown');
     }, 'keydown');
   }
   }
 
 
-  showDashEditView(view) {
-    var search = _.extend(this.$location.search(), {editview: view});
+  showDashEditView() {
+    var search = _.extend(this.$location.search(), {editview: 'settings'});
     this.$location.search(search);
     this.$location.search(search);
   }
   }
 
 
@@ -84,10 +84,6 @@ export class KeybindingSrv {
       scope.broadcastRefresh();
       scope.broadcastRefresh();
     });
     });
 
 
-    this.bind('mod+h', () => {
-      dashboard.hideControls = !dashboard.hideControls;
-    });
-
     this.bind('mod+s', e => {
     this.bind('mod+s', e => {
       scope.appEvent('save-dashboard');
       scope.appEvent('save-dashboard');
     });
     });
@@ -197,7 +193,7 @@ export class KeybindingSrv {
     });
     });
 
 
     this.bind('d s', () => {
     this.bind('d s', () => {
-      this.showDashEditView('settings');
+      this.showDashEditView();
     });
     });
 
 
     this.bind('d k', () => {
     this.bind('d k', () => {
@@ -215,8 +211,14 @@ export class KeybindingSrv {
       }
       }
 
 
       scope.appEvent('hide-modal');
       scope.appEvent('hide-modal');
-      scope.appEvent('hide-dash-editor');
       scope.appEvent('panel-change-view', {fullscreen: false, edit: false});
       scope.appEvent('panel-change-view', {fullscreen: false, edit: false});
+
+      // close settings view
+      var search = this.$location.search();
+      if (search.editview) {
+        delete search.editview;
+        this.$location.search(search);
+      }
     });
     });
   }
   }
 }
 }

+ 7 - 0
public/app/core/services/popover_srv.ts

@@ -68,6 +68,13 @@ function popoverSrv($compile, $rootScope, $timeout) {
       openDrop = drop;
       openDrop = drop;
       openDrop.open();
       openDrop.open();
     }, 100);
     }, 100);
+
+    // return close function
+    return function() {
+      if (drop) {
+        drop.close();
+      }
+    };
   };
   };
 }
 }
 
 

+ 1 - 1
public/app/features/annotations/annotation_tooltip.ts

@@ -4,7 +4,7 @@ import coreModule from 'app/core/core_module';
 import alertDef from '../alerting/alert_def';
 import alertDef from '../alerting/alert_def';
 
 
 /** @ngInject **/
 /** @ngInject **/
-export function annotationTooltipDirective($sanitize, dashboardSrv, contextSrv, popoverSrv, $compile) {
+export function annotationTooltipDirective($sanitize, dashboardSrv, contextSrv, $compile) {
 
 
   function sanitizeString(str) {
   function sanitizeString(str) {
     try {
     try {

+ 5 - 14
public/app/features/annotations/editor_ctrl.ts

@@ -26,7 +26,7 @@ export class AnnotationsEditorCtrl {
   ];
   ];
 
 
   /** @ngInject */
   /** @ngInject */
-  constructor(private $scope, private datasourceSrv) {
+  constructor($scope, private datasourceSrv) {
     $scope.ctrl = this;
     $scope.ctrl = this;
 
 
     this.mode = 'list';
     this.mode = 'list';
@@ -62,7 +62,6 @@ export class AnnotationsEditorCtrl {
   update() {
   update() {
     this.reset();
     this.reset();
     this.mode = 'list';
     this.mode = 'list';
-    this.$scope.broadcastRefresh();
   }
   }
 
 
   setupNew() {
   setupNew() {
@@ -70,32 +69,24 @@ export class AnnotationsEditorCtrl {
     this.reset();
     this.reset();
   }
   }
 
 
+  backToList() {
+    this.mode = 'list';
+  }
+
   add() {
   add() {
     this.annotations.push(this.currentAnnotation);
     this.annotations.push(this.currentAnnotation);
     this.reset();
     this.reset();
     this.mode = 'list';
     this.mode = 'list';
-    this.$scope.broadcastRefresh();
-    this.$scope.dashboard.updateSubmenuVisibility();
   }
   }
 
 
   removeAnnotation(annotation) {
   removeAnnotation(annotation) {
     var index = _.indexOf(this.annotations, annotation);
     var index = _.indexOf(this.annotations, annotation);
     this.annotations.splice(index, 1);
     this.annotations.splice(index, 1);
-    this.$scope.dashboard.updateSubmenuVisibility();
-    this.$scope.broadcastRefresh();
   }
   }
 
 
   onColorChange(newColor) {
   onColorChange(newColor) {
     this.currentAnnotation.iconColor = newColor;
     this.currentAnnotation.iconColor = newColor;
   }
   }
-
-  annotationEnabledChange() {
-    this.$scope.broadcastRefresh();
-  }
-
-  annotationHiddenChanged() {
-    this.$scope.dashboard.updateSubmenuVisibility();
-  }
 }
 }
 
 
 coreModule.controller('AnnotationsEditorCtrl', AnnotationsEditorCtrl);
 coreModule.controller('AnnotationsEditorCtrl', AnnotationsEditorCtrl);

+ 79 - 111
public/app/features/annotations/partials/editor.html

@@ -1,144 +1,112 @@
-<div ng-controller="AnnotationsEditorCtrl">
-	<div class="tabbed-view-header">
-		<h2 class="tabbed-view-title">
-			Annotations
-		</h2>
-
-		<ul class="gf-tabs">
-			<li class="gf-tabs-item" >
-				<a class="gf-tabs-link" ng-click="ctrl.mode = 'list';" ng-class="{active: ctrl.mode === 'list'}">
-					Queries
-				</a>
-			</li>
-			<li class="gf-tabs-item" ng-show="ctrl.mode === 'edit'">
-				<a class="gf-tabs-link" ng-class="{active: ctrl.mode === 'edit'}">
-					Edit Query
-				</a>
-			</li>
-			<li class="gf-tabs-item" ng-show="ctrl.mode === 'new'">
-				<span class="active gf-tabs-link">New Query</span>
-			</li>
-
-			<li class="gf-tabs-item" >
-        <a class="gf-tabs-link" ng-click="ctrl.mode = 'help';" ng-class="{active: ctrl.mode === 'help'}">
-          Help
-        </a>
-      </li>
-		</ul>
-
-		<button class="tabbed-view-close-btn" ng-click="dismiss();">
-			<i class="fa fa-remove"></i>
-		</button>
-	</div>
 
 
-	<div class="tabbed-view-body">
+<div ng-controller="AnnotationsEditorCtrl">
+	<h3 class="dashboard-settings__header">
+		<a ng-click="ctrl.backToList()">Annotations</a>
+		<span ng-show="ctrl.mode === 'new'">&gt; New</span>
+		<span ng-show="ctrl.mode === 'edit'">&gt; Edit</span>
+	</h3>
 
 
-		<div ng-show="ctrl.mode === 'help'">
-			<div class="grafana-info-box col-lg-8">
-				<h5>What are Annotations?</h5>
-				<p>
-					Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines and icons
-					on all graph panels. When you hover over an annotation icon you can get title, tags, and text information for the event.
-					In the <i>Queries</i> tab you can add queries that return annotation events.
-				</p>
-				<p>
-					You can add annotations directly from grafana by holding CTRL or CMD + click on graph (or drag region). These will be stored in Grafana's annotation database.
-				</p>
-				Checkout the <a class="external-link" target="_blank" href="http://docs.grafana.org/reference/annotations/">Annotations documentation</a> for more information.
-			</div>
+	<div ng-if="ctrl.mode === 'list'">
+		<div class="page-action-bar" ng-if="ctrl.annotations.length > 1">
+			<div class="page-action-bar__spacer"></div>
+			<a type="button" class="btn btn-success" ng-click="ctrl.setupNew();"><i class="fa fa-plus" ></i> New</a>
 		</div>
 		</div>
 
 
-		<div class="editor-row row" ng-if="ctrl.mode === 'list'">
-			<div ng-if="ctrl.annotations.length === 0">
-				<em>No annotation queries defined</em>
-			</div>
-			<table class="grafana-options-table">
+		<table class="filter-table filter-table--hover">
+			<thead>
+				<tr>
+					<th>Query name</th>
+					<th>Data source</th>
+					<th colspan="3"></th>
+				</tr>
+			</thead>
+			<tbody>
 				<tr ng-repeat="annotation in ctrl.annotations">
 				<tr ng-repeat="annotation in ctrl.annotations">
-					<td style="width:90%" ng-hide="annotation.builtIn">
+					<td style="width:90%" ng-hide="annotation.builtIn" class="pointer" ng-click="ctrl.edit(annotation)">
 						<i class="fa fa-comment" style="color:{{annotation.iconColor}}"></i> &nbsp;
 						<i class="fa fa-comment" style="color:{{annotation.iconColor}}"></i> &nbsp;
 						{{annotation.name}}
 						{{annotation.name}}
 					</td>
 					</td>
-					<td style="width:90%" ng-show="annotation.builtIn">
+					<td style="width:90%" ng-show="annotation.builtIn" class="pointer" ng-click="ctrl.edit(annotation)">
 						<i class="fa fa-comment"></i> &nbsp;
 						<i class="fa fa-comment"></i> &nbsp;
 						<em class="muted">{{annotation.name}} (Built-in)</em>
 						<em class="muted">{{annotation.name}} (Built-in)</em>
 					</td>
 					</td>
+					<td class="pointer" ng-click="ctrl.edit(annotation)">
+						{{annotation.datasource || 'Default'}}
+					</td>
 					<td style="width: 1%"><i ng-click="_.move(ctrl.annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
 					<td style="width: 1%"><i ng-click="_.move(ctrl.annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
 					<td style="width: 1%"><i ng-click="_.move(ctrl.annotations,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
 					<td style="width: 1%"><i ng-click="_.move(ctrl.annotations,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
-					<td style="width: 1%">
-						<a ng-click="ctrl.edit(annotation)" class="btn btn-inverse btn-mini">
-							<i class="fa fa-edit"></i>
-							Edit
-						</a>
-					</td>
 					<td style="width: 1%">
 					<td style="width: 1%">
 						<a ng-click="ctrl.removeAnnotation(annotation)" class="btn btn-danger btn-mini" ng-hide="annotation.builtIn">
 						<a ng-click="ctrl.removeAnnotation(annotation)" class="btn btn-danger btn-mini" ng-hide="annotation.builtIn">
 							<i class="fa fa-remove"></i>
 							<i class="fa fa-remove"></i>
 						</a>
 						</a>
 					</td>
 					</td>
 				</tr>
 				</tr>
-			</table>
-		</div>
+			</tbody>
+		</table>
 
 
-		<div class="gf-form" ng-show="ctrl.mode === 'list'">
-			<div class="gf-form-button-row">
-				<a type="button" class="btn gf-form-button btn-success" ng-click="ctrl.setupNew()"><i class="fa fa-plus" ></i>&nbsp;&nbsp;New</a>
+		<!-- empty list cta, there is always one built in query -->
+		<div ng-if="ctrl.annotations.length === 1" class="p-t-2">
+			<div class="empty-list-cta">
+				<div class="empty-list-cta__title">There are no custom annotation queries added yet</div>
+				<a ng-click="ctrl.setupNew()" class="empty-list-cta__button btn btn-xlarge btn-success">
+					<i class="gicon gicon-dashboard-new"></i>
+					Add Annotation Query
+				</a>
+				<div class="grafana-info-box">
+					<h5>What are Annotations?</h5>
+					<p>
+					Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines and icons
+					on all graph panels. When you hover over an annotation icon you can get event text &amp; tags for the event. You can add annotation events
+					directly from grafana by holding CTRL or CMD + click on graph (or drag region). These will be stored in Grafana's annotation database.
+					</p>
+					Checkout the <a class="external-link" target="_blank" href="http://docs.grafana.org/reference/annotations/">Annotations documentation</a> for more information.
+				</div>
 			</div>
 			</div>
 		</div>
 		</div>
+	</div>
 
 
-		<div class="annotations-basic-settings" ng-if="ctrl.mode === 'edit' || ctrl.mode === 'new'">
-			<div>
-				<div class="gf-form-group">
-					<h5 class="section-heading">General</h5>
-					<div class="gf-form-inline">
-						<div class="gf-form">
-							<span class="gf-form-label width-7">Name</span>
-							<input type="text" class="gf-form-input width-20" ng-model='ctrl.currentAnnotation.name' placeholder="name"></input>
-						</div>
-						<div class="gf-form">
-							<span class="gf-form-label width-7">Data source</span>
-							<div class="gf-form-select-wrapper">
-								<select class="gf-form-input" ng-model="ctrl.currentAnnotation.datasource" ng-options="f.name as f.name for f in ctrl.datasources" ng-change="ctrl.datasourceChanged()"></select>
-							</div>
-						</div>
+	<div class="annotations-basic-settings" ng-if="ctrl.mode === 'edit' || ctrl.mode === 'new'">
+		<div class="gf-form-group">
+			<h5 class="section-heading">General</h5>
+			<div class="gf-form-inline">
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Name</span>
+					<input type="text" class="gf-form-input width-20" ng-model='ctrl.currentAnnotation.name' placeholder="name"></input>
+				</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Data source</span>
+					<div class="gf-form-select-wrapper">
+						<select class="gf-form-input" ng-model="ctrl.currentAnnotation.datasource" ng-options="f.name as f.name for f in ctrl.datasources" ng-change="ctrl.datasourceChanged()"></select>
 					</div>
 					</div>
 				</div>
 				</div>
+			</div>
+		</div>
 
 
-				<div class="gf-form-group">
-					<div class="gf-form-inline">
-						<gf-form-switch class="gf-form"
-														label="Enabled"
-														checked="ctrl.currentAnnotation.enable"
-														on-change="ctrl.annotationEnabledChange()"
-														label-class="width-7">
-						</gf-form-switch>
-						<gf-form-switch class="gf-form"
-														label="Hidden"
-														tooltip="Hides the annotation query toggle from showing at the top of the dashboard"
-														checked="ctrl.currentAnnotation.hide"
-														on-change="ctrl.annotationHiddenChanged()"
-														label-class="width-7">
-						</gf-form-switch>
-						<div class="gf-form">
-							<label class="gf-form-label width-9">Color</label>
-							<span class="gf-form-label">
-								<color-picker color="ctrl.currentAnnotation.iconColor" onChange="ctrl.onColorChange"></color-picker>
-							</span>
-						</div>
-					</div>
+		<div class="gf-form-group">
+			<div class="gf-form-inline">
+				<gf-form-switch class="gf-form" label="Enabled" checked="ctrl.currentAnnotation.enable" label-class="width-7">
+				</gf-form-switch>
+				<gf-form-switch class="gf-form" label="Hidden" tooltip="Hides the annotation query toggle from showing at the top of the dashboard" checked="ctrl.currentAnnotation.hide" label-class="width-7">
+				</gf-form-switch>
+				<div class="gf-form">
+					<label class="gf-form-label width-9">Color</label>
+					<span class="gf-form-label">
+						<color-picker color="ctrl.currentAnnotation.iconColor" onChange="ctrl.onColorChange"></color-picker>
+					</span>
 				</div>
 				</div>
 			</div>
 			</div>
+		</div>
 
 
-			<h5 class="section-heading">Query</h5>
-			<rebuild-on-change property="ctrl.currentDatasource">
-				<plugin-component type="annotations-query-ctrl">
-				</plugin-component>
-			</rebuild-on-change>
+		<h5 class="section-heading">Query</h5>
+		<rebuild-on-change property="ctrl.currentDatasource">
+			<plugin-component type="annotations-query-ctrl">
+			</plugin-component>
+		</rebuild-on-change>
 
 
-			<div class="gf-form">
-				<div class="gf-form-button-row p-y-0">
-					<button ng-show="ctrl.mode === 'new'" type="button" class="btn gf-form-button btn-success" ng-click="ctrl.add()">Add</button>
-					<button ng-show="ctrl.mode === 'edit'" type="button" class="btn btn-success pull-left" ng-click="ctrl.update()">Update</button>
-				</div>
+		<div class="gf-form">
+			<div class="gf-form-button-row p-y-0">
+				<button ng-show="ctrl.mode === 'new'" type="button" class="btn gf-form-button btn-success" ng-click="ctrl.add()">Add</button>
+				<button ng-show="ctrl.mode === 'edit'" type="button" class="btn btn-success pull-left" ng-click="ctrl.update()">Update</button>
 			</div>
 			</div>
 		</div>
 		</div>
 	</div>
 	</div>

+ 2 - 1
public/app/features/dashboard/all.ts

@@ -26,8 +26,9 @@ import './acl/acl';
 import './folder_picker/picker';
 import './folder_picker/picker';
 import './folder_modal/folder';
 import './folder_modal/folder';
 import './move_to_folder_modal/move_to_folder';
 import './move_to_folder_modal/move_to_folder';
-import coreModule from 'app/core/core_module';
+import './settings/settings';
 
 
+import coreModule from 'app/core/core_module';
 import {DashboardListCtrl} from './dashboard_list_ctrl';
 import {DashboardListCtrl} from './dashboard_list_ctrl';
 import {FolderDashboardsCtrl} from './folder_dashboards_ctrl';
 import {FolderDashboardsCtrl} from './folder_dashboards_ctrl';
 import {FolderPermissionsCtrl} from './folder_permissions_ctrl';
 import {FolderPermissionsCtrl} from './folder_permissions_ctrl';

+ 0 - 6
public/app/features/dashboard/dashboard_ctrl.ts

@@ -122,12 +122,6 @@ export class DashboardCtrl implements PanelContainer {
       this.$rootScope.$broadcast("refresh");
       this.$rootScope.$broadcast("refresh");
     }
     }
 
 
-    onFolderChange(folder) {
-      this.dashboard.folderId = folder.id;
-      this.dashboard.meta.folderId = folder.id;
-      this.dashboard.meta.folderTitle= folder.title;
-    }
-
     getPanelContainer() {
     getPanelContainer() {
       return this;
       return this;
     }
     }

+ 0 - 2
public/app/features/dashboard/dashboard_model.ts

@@ -22,7 +22,6 @@ export class DashboardModel {
   graphTooltip: any;
   graphTooltip: any;
   time: any;
   time: any;
   timepicker: any;
   timepicker: any;
-  hideControls: any;
   templating: any;
   templating: any;
   annotations: any;
   annotations: any;
   refresh: any;
   refresh: any;
@@ -67,7 +66,6 @@ export class DashboardModel {
     this.timezone = data.timezone || '';
     this.timezone = data.timezone || '';
     this.editable = data.editable !== false;
     this.editable = data.editable !== false;
     this.graphTooltip = data.graphTooltip || 0;
     this.graphTooltip = data.graphTooltip || 0;
-    this.hideControls = data.hideControls || false;
     this.time = data.time || {from: 'now-6h', to: 'now'};
     this.time = data.time || {from: 'now-6h', to: 'now'};
     this.timepicker = data.timepicker || {};
     this.timepicker = data.timepicker || {};
     this.templating = this.ensureListExist(data.templating);
     this.templating = this.ensureListExist(data.templating);

+ 13 - 18
public/app/features/dashboard/dashnav/dashnav.html

@@ -27,40 +27,35 @@
 			<i class="gicon gicon-add-panel"></i>
 			<i class="gicon gicon-add-panel"></i>
 		</button>
 		</button>
 
 
-		<button class="btn navbar-button" ng-show="::ctrl.dashboard.meta.canStar" ng-click="ctrl.starDashboard()" bs-tooltip="'Mark as favorite'" data-placement="bottom">
+		<button class="btn navbar-button navbar-button--star" ng-show="::ctrl.dashboard.meta.canStar" ng-click="ctrl.starDashboard()" bs-tooltip="'Mark as favorite'" data-placement="bottom">
 			<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}"></i>
 			<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}"></i>
 		</button>
 		</button>
 
 
-		<button class="btn navbar-button" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
+		<button class="btn navbar-button navbar-button--share" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
 			<i class="fa fa-share-square-o"></i></a>
 			<i class="fa fa-share-square-o"></i></a>
 		</button>
 		</button>
 
 
-		<button class="btn navbar-button" ng-show="::ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
+		<button class="btn navbar-button navbar-button--save" ng-show="::ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
 			<i class="fa fa-save"></i>
 			<i class="fa fa-save"></i>
 		</button>
 		</button>
 
 
-		<button class="btn navbar-button" ng-if="::ctrl.dashboard.snapshot.originalUrl" ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom">
+		<button class="btn navbar-button navbar-button--snapshot-origin" ng-if="::ctrl.dashboard.snapshot.originalUrl" ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom">
 			<i class="fa fa-link"></i>
 			<i class="fa fa-link"></i>
 		</button>
 		</button>
 
 
-		<div class="dropdown">
-			<button class="btn navbar-button" data-toggle="dropdown" bs-tooltip="'Settings'" data-placement="bottom">
-				<i class="fa fa-cog"></i>
-			</button>
-
-			<ul class="dropdown-menu dropdown-menu--navbar">
-				<li ng-repeat="navItem in ::ctrl.navModel.menu" ng-class="{active: navItem.active}">
-					<a class="pointer" ng-href="{{::navItem.url}}" ng-click="ctrl.navItemClicked(navItem, $event)">
-						<i class="{{::navItem.icon}}" ng-show="::navItem.icon"></i>
-						{{::navItem.title}}
-					</a>
-				</li>
-			</ul>
-		</div>
+		<button class="btn navbar-button navbar-button--settings" ng-click="ctrl.toggleSettings()" bs-tooltip="'Settings'" data-placement="bottom">
+			<i class="fa fa-cog"></i>
+		</button>
 	</div>
 	</div>
 
 
 	<gf-time-picker class="gf-timepicker-nav" dashboard="ctrl.dashboard" ng-if="!ctrl.dashboard.timepicker.hidden"></gf-time-picker>
 	<gf-time-picker class="gf-timepicker-nav" dashboard="ctrl.dashboard" ng-if="!ctrl.dashboard.timepicker.hidden"></gf-time-picker>
 
 
+	<div class="navbar-buttons navbar-buttons--close">
+		<button class="btn navbar-button navbar-button--primary" ng-click="ctrl.close()" bs-tooltip="'Back to dashboard'" data-placement="bottom">
+			<i class="fa fa-reply"></i>
+		</button>
+	</div>
+
 </div>
 </div>
 
 
 <dashboard-search></dashboard-search>
 <dashboard-search></dashboard-search>

+ 17 - 70
public/app/features/dashboard/dashnav/dashnav.ts

@@ -1,4 +1,3 @@
-import _ from 'lodash';
 import moment from 'moment';
 import moment from 'moment';
 import angular from 'angular';
 import angular from 'angular';
 import {appEvents, NavModel} from 'app/core/core';
 import {appEvents, NavModel} from 'app/core/core';
@@ -15,13 +14,11 @@ export class DashNavCtrl {
     private $rootScope,
     private $rootScope,
     private dashboardSrv,
     private dashboardSrv,
     private $location,
     private $location,
-    private backendSrv,
     public playlistSrv,
     public playlistSrv,
     navModelSrv) {
     navModelSrv) {
       this.navModel = navModelSrv.getDashboardNav(this.dashboard, this);
       this.navModel = navModelSrv.getDashboardNav(this.dashboard, this);
 
 
       appEvents.on('save-dashboard', this.saveDashboard.bind(this), $scope);
       appEvents.on('save-dashboard', this.saveDashboard.bind(this), $scope);
-      appEvents.on('delete-dashboard', this.deleteDashboard.bind(this), $scope);
 
 
       if (this.dashboard.meta.isSnapshot) {
       if (this.dashboard.meta.isSnapshot) {
         var meta = this.dashboard.meta;
         var meta = this.dashboard.meta;
@@ -32,13 +29,26 @@ export class DashNavCtrl {
       }
       }
     }
     }
 
 
-    openEditView(editview) {
-      var search = _.extend(this.$location.search(), {editview: editview});
+    toggleSettings() {
+      let search = this.$location.search();
+      if (search.editview) {
+        delete search.editview;
+      } else {
+        search.editview = 'settings';
+      }
       this.$location.search(search);
       this.$location.search(search);
     }
     }
 
 
-    showHelpModal() {
-      appEvents.emit('show-modal', {templateHtml: '<help-modal></help-modal>'});
+    close() {
+      let search = this.$location.search();
+      if (search.editview) {
+        delete search.editview;
+      }
+      if (search.fullscreen) {
+        delete search.fullscreen;
+        delete search.edit;
+      }
+      this.$location.search(search);
     }
     }
 
 
     starDashboard() {
     starDashboard() {
@@ -63,73 +73,10 @@ export class DashNavCtrl {
       angular.element(evt.currentTarget).tooltip('hide');
       angular.element(evt.currentTarget).tooltip('hide');
     }
     }
 
 
-    makeEditable() {
-      this.dashboard.editable = true;
-
-      return this.dashboardSrv.saveDashboard({makeEditable: true, overwrite: false}).then(() => {
-        // force refresh whole page
-        window.location.href = window.location.href;
-      });
-    }
-
-    exitFullscreen() {
-      this.$rootScope.appEvent('panel-change-view', {fullscreen: false, edit: false});
-    }
-
     saveDashboard() {
     saveDashboard() {
       return this.dashboardSrv.saveDashboard();
       return this.dashboardSrv.saveDashboard();
     }
     }
 
 
-    deleteDashboard() {
-      var confirmText = '';
-      var text2 = this.dashboard.title;
-
-      const alerts = _.sumBy(this.dashboard.panels, panel => {
-         return panel.alert ? 1 : 0;
-      });
-
-      if (alerts > 0) {
-        confirmText = 'DELETE';
-        text2 = `This dashboard contains ${alerts} alerts. Deleting this dashboard will also delete those alerts`;
-      }
-
-      appEvents.emit('confirm-modal', {
-        title: 'Delete',
-        text: 'Do you want to delete this dashboard?',
-        text2: text2,
-        icon: 'fa-trash',
-        confirmText: confirmText,
-        yesText: 'Delete',
-        onConfirm: () => {
-          this.dashboard.meta.canSave = false;
-          this.deleteDashboardConfirmed();
-        }
-      });
-    }
-
-    deleteDashboardConfirmed() {
-      this.backendSrv.delete('/api/dashboards/db/' + this.dashboard.meta.slug).then(() => {
-        appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']);
-        this.$location.url('/');
-      });
-    }
-
-    saveDashboardAs() {
-      return this.dashboardSrv.showSaveAsModal();
-    }
-
-    viewJson() {
-      var clone = this.dashboard.getSaveModelClone();
-
-      this.$rootScope.appEvent('show-json-editor', {
-        object: clone,
-      });
-    }
-
-    onFolderChange(folderId) {
-      this.dashboard.folderId = folderId;
-    }
-
     showSearch() {
     showSearch() {
       this.$rootScope.appEvent('show-dash-search');
       this.$rootScope.appEvent('show-dash-search');
     }
     }

+ 105 - 133
public/app/features/dashboard/history/history.html

@@ -1,146 +1,118 @@
-<div class="tabbed-view-header">
-	<h2 class="tabbed-view-title">
-		Version history
-	</h2>
+<h3 class="dashboard-settings__header">
+  <a ng-click="ctrl.switchMode('list')">Versions</a>
+  <span ng-show="ctrl.mode === 'compare'">
+    &gt; Comparing {{ctrl.baseInfo.version}}
+    <i class="fa fa-arrows-h"></i>
+    {{ctrl.newInfo.version}}
+    <cite class="muted" ng-if="ctrl.isNewLatest">(Latest)</cite>
+  </span>
+</h3>
 
 
-	<ul class="gf-tabs">
-		<li class="gf-tabs-item" >
-			<a class="gf-tabs-link" ng-click="ctrl.switchMode('list');" ng-class="{active: ctrl.mode === 'list'}">
-				List
-			</a>
-		</li>
-		<li class="gf-tabs-item" ng-show="ctrl.mode === 'compare'">
-			<span class="active gf-tabs-link">
-				Version Comparison
-			</span>
-		</li>
-	</ul>
-
-	<button class="tabbed-view-close-btn" ng-click="ctrl.dismiss();">
-		<i class="fa fa-remove"></i>
-	</button>
-</div>
+<div ng-if="ctrl.mode === 'list'">
+  <div ng-if="ctrl.loading">
+    <i class="fa fa-spinner fa-spin"></i>
+    <em>Fetching history list&hellip;</em>
+  </div>
 
 
-<div class="tabbed-view-body">
+  <div ng-if="!ctrl.loading">
+    <div class="gf-form-group">
+      <table class="filter-table">
+        <thead>
+          <tr>
+            <th class="width-4"></th>
+            <th class="width-4">Version</th>
+            <th class="width-14">Date</th>
+            <th class="width-10">Updated By</th>
+            <th>Notes</th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr ng-repeat="revision in ctrl.revisions">
+            <td class="filter-table__switch-cell" bs-tooltip="!revision.checked && ctrl.canCompare ? 'You can only compare 2 versions at a time' : ''" data-placement="right">
+              <gf-form-switch switch-class="gf-form-switch--table-cell" checked="revision.checked" on-change="ctrl.revisionSelectionChanged()" ng-disabled="!revision.checked && ctrl.canCompare">
+              </gf-form-switch>
+            </td>
+            <td class="text-center">{{revision.version}}</td>
+            <td>{{revision.createdDateString}}</td>
+            <td>{{revision.createdBy}}</td>
+            <td>{{revision.message}}</td>
+            <td class="text-right">
+              <a class="btn btn-inverse btn-small" ng-show="revision.version !== ctrl.dashboard.version" ng-click="ctrl.restore(revision.version)">
+                <i class="fa fa-history"></i>&nbsp;&nbsp;Restore
+              </a>
+              <a class="btn btn-outline-disabled btn-small" ng-show="revision.version === ctrl.dashboard.version">
+                <i class="fa fa-check"></i>&nbsp;&nbsp;Latest
+              </a>
+            </td>
+          </tr>
+        </tbody>
+      </table>
 
 
-	<div ng-if="ctrl.mode === 'list'">
-		<div ng-if="ctrl.loading">
-			<i class="fa fa-spinner fa-spin"></i>
-			<em>Fetching history list&hellip;</em>
-		</div>
+      <div ng-if="ctrl.appending">
+        <i class="fa fa-spinner fa-spin"></i>
+        <em>Fetching more entries&hellip;</em>
+      </div>
 
 
-		<div ng-if="!ctrl.loading">
       <div class="gf-form-group">
       <div class="gf-form-group">
-        <table class="filter-table">
-          <thead>
-            <tr>
-              <th class="width-4"></th>
-              <th class="width-4">Version</th>
-              <th class="width-14">Date</th>
-              <th class="width-10">Updated By</th>
-              <th>Notes</th>
-              <th></th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr ng-repeat="revision in ctrl.revisions">
-              <td class="filter-table__switch-cell" bs-tooltip="!revision.checked && ctrl.canCompare ? 'You can only compare 2 versions at a time' : ''" data-placement="right">
-                <gf-form-switch switch-class="gf-form-switch--table-cell"
-                                checked="revision.checked"
-                                on-change="ctrl.revisionSelectionChanged()"
-																ng-disabled="!revision.checked && ctrl.canCompare">
-                </gf-form-switch>
-              </td>
-              <td class="text-center">{{revision.version}}</td>
-              <td>{{revision.createdDateString}}</td>
-              <td>{{revision.createdBy}}</td>
-              <td>{{revision.message}}</td>
-              <td class="text-right">
-                <a class="btn btn-inverse btn-small" ng-show="revision.version !== ctrl.dashboard.version" ng-click="ctrl.restore(revision.version)">
-                  <i class="fa fa-history"></i>&nbsp;&nbsp;Restore
-                </a>
-                <a class="btn btn-outline-disabled btn-small" ng-show="revision.version === ctrl.dashboard.version">
-                  <i class="fa fa-check"></i>&nbsp;&nbsp;Latest
-                </a>
-              </td>
-            </tr>
-          </tbody>
-        </table>
-
-        <div ng-if="ctrl.appending">
-          <i class="fa fa-spinner fa-spin"></i>
-          <em>Fetching more entries&hellip;</em>
-        </div>
-
-        <div class="gf-form-group" ng-show="ctrl.mode === 'list'">
-          <div class="gf-form-button-row">
-            <button	type="button"
-                class="btn gf-form-button btn-secondary"
-                ng-if="ctrl.revisions.length > 1"
-                ng-disabled="!ctrl.canCompare"
-                ng-click="ctrl.getDiff(ctrl.diff)"
-								bs-tooltip="ctrl.canCompare ? '' : 'Select 2 versions to start comparing'" data-placement="bottom">
-              <i class="fa fa-code-fork" ></i>&nbsp;&nbsp;Compare versions
-            </button>
-            <button  type="button"
-                class="btn gf-form-button btn-inverse"
-                ng-if="ctrl.revisions.length >= ctrl.limit"
-                ng-click="ctrl.addToLog()"
-                ng-disabled="ctrl.isLastPage()">
-              Show more versions
-            </button>
-          </div>
+        <div class="gf-form-button-row">
+          <button  type="button"
+                   class="btn gf-form-button btn-inverse"
+                   ng-if="ctrl.revisions.length >= ctrl.limit"
+                   ng-click="ctrl.addToLog()"
+                   ng-disabled="ctrl.isLastPage()">
+            Show more versions
+          </button>
+          <button type="button"
+                  class="btn btn-success"
+                  ng-if="ctrl.revisions.length > 1"
+                  ng-disabled="!ctrl.canCompare"
+                  ng-click="ctrl.getDiff(ctrl.diff)"
+                  bs-tooltip="ctrl.canCompare ? '' : 'Select 2 versions to start comparing'" data-placement="bottom">
+            <i class="fa fa-code-fork" ></i>&nbsp;&nbsp;Compare versions
+          </button>
         </div>
         </div>
       </div>
       </div>
-		</div>
-	</div>
+    </div>
+  </div>
+</div>
 
 
-	<div class="edit-tab-with-sidemenu" ng-if="ctrl.mode === 'compare'">
-    <aside class="edit-sidemenu-aside">
-      <ul class="edit-sidemenu">
-        <li ng-class="{active: ctrl.diff === 'basic'}"><a ng-click="ctrl.getDiff('basic')" href="">Change Summary</a></li>
-        <li ng-class="{active: ctrl.diff === 'html'}"><a ng-click="ctrl.getDiff('json')" href="">JSON Diff</a></li>
-      </ul>
-    </aside>
+<div ng-if="ctrl.mode === 'compare'">
+  <div ng-if="ctrl.loading">
+    <i class="fa fa-spinner fa-spin"></i>
+    <em>Fetching changes&hellip;</em>
+  </div>
 
 
-    <div class="edit-tab-content">
-      <div ng-if="ctrl.loading">
-        <i class="fa fa-spinner fa-spin"></i>
-        <em>Fetching changes&hellip;</em>
-      </div>
+  <div ng-if="!ctrl.loading">
+    <button  type="button"
+             class="btn btn-danger pull-right"
+             ng-click="ctrl.restore(ctrl.baseInfo.version)"
+             ng-if="ctrl.isNewLatest">
+      <i class="fa fa-history" ></i>&nbsp;&nbsp;Restore to version {{ctrl.baseInfo.version}}
+    </button>
+    <section>
+      <p class="small muted">
+      <strong>Version {{ctrl.newInfo.version}}</strong> updated by
+      <span>{{ctrl.newInfo.createdBy}} </span>
+      <span>{{ctrl.newInfo.ageString}}</span>
+      <span> - {{ctrl.newInfo.message}}</span>
+      </p>
+      <p class="small muted">
+      <strong>Version {{ctrl.baseInfo.version}}</strong> updated by
+      <span>{{ctrl.baseInfo.createdBy}} </span>
+      <span>{{ctrl.baseInfo.ageString}}</span>
+      <span> - {{ctrl.baseInfo.message}}</span>
+      </p>
+    </section>
 
 
-      <div ng-if="!ctrl.loading">
-        <a  type="button"
-            class="btn gf-form-button btn-secondary pull-right"
-            ng-click="ctrl.restore(ctrl.baseInfo.version)"
-            ng-if="ctrl.isNewLatest">
-          <i class="fa fa-history" ></i>&nbsp;&nbsp;Restore to version {{ctrl.baseInfo.version}}
-        </a>
-        <h4>
-          Comparing Version {{ctrl.baseInfo.version}}
-          <i class="fa fa-arrows-h"></i>
-					Version {{ctrl.newInfo.version}}
-          <cite class="muted" ng-if="ctrl.isNewLatest">(Latest)</cite>
-        </h4>
-        <section>
-          <p class="small muted">
-          <strong>Version {{ctrl.newInfo.version}}</strong> updated by
-          <span>{{ctrl.newInfo.createdBy}} </span>
-          <span>{{ctrl.newInfo.ageString}}</span>
-          <span> - {{ctrl.newInfo.message}}</span>
-          </p>
-          <p class="small muted">
-          <strong>Version {{ctrl.baseInfo.version}}</strong> updated by
-          <span>{{ctrl.baseInfo.createdBy}} </span>
-          <span>{{ctrl.baseInfo.ageString}}</span>
-          <span> - {{ctrl.baseInfo.message}}</span>
-          </p>
-        </section>
-        <div id="delta" diff-delta>
-          <div class="delta-basic" ng-show="ctrl.diff === 'basic'" compile="ctrl.delta.basic"></div>
-          <div class="delta-html" ng-show="ctrl.diff === 'json'" compile="ctrl.delta.json"></div>
-        </div>
-      </div>
+    <div id="delta" diff-delta>
+      <div class="delta-basic" compile="ctrl.delta.basic"></div>
+    </div>
+
+    <div class="gf-form-button-row">
+      <button class="btn btn-secondary" ng-click="ctrl.getDiff('json')">View JSON Diff</button>
     </div>
     </div>
+
+    <div class="delta-html" ng-show="ctrl.diff === 'json'" compile="ctrl.delta.json"></div>
   </div>
   </div>
 </div>
 </div>

+ 0 - 2
public/app/features/dashboard/history/history.ts

@@ -1,5 +1,3 @@
-///<reference path="../../../headers/common.d.ts" />
-
 import './history_srv';
 import './history_srv';
 
 
 import _ from 'lodash';
 import _ from 'lodash';

+ 0 - 65
public/app/features/dashboard/partials/addAnnotationModal.html

@@ -1,65 +0,0 @@
-<div class="modal-body" ng-controller="AddAnnotationModalCtrl">
-
-  <div class="modal-header">
-    <h2 class="modal-header-title">
-      Add Annotation
-    </h2>
-
-    <a class="modal-header-close" ng-click="ctrl.close()">
-      <i class="fa fa-remove"></i>
-    </a>
-  </div>
-
-
-  <div class="modal-content">
-    <div class="share-modal-body">
-      <div class="share-modal-header">
-
-        <div class="share-modal-big-icon">
-          <i class="fa fa-tag"></i>
-        </div>
-
-        <div class="share-modal-content">
-
-          <div class="gf-form-group share-modal-options">
-            <p class="share-modal-info-text">
-              Add annotation details.
-            </p>
-
-            <div class="gf-form">
-              <span class="gf-form-label width-8">Title</span>
-              <input type="text" ng-model="ctrl.annotation.title" class="gf-form-input max-width-20">
-            </div>
-            <div class="gf-form">
-              <span class="gf-form-label width-8" ng-if="!ctrl.annotation.timeTo">Time</span>
-              <span class="gf-form-label width-8" ng-if="ctrl.annotation.timeTo">Time Start</span>
-              <input type="text" ng-model="ctrl.annotation.time" class="gf-form-input max-width-20">
-            </div>
-            <div class="gf-form" ng-if="ctrl.annotation.timeTo">
-              <span class="gf-form-label width-8">Time Stop</span>
-              <input type="text" ng-model="ctrl.annotation.timeTo" class="gf-form-input max-width-20">
-            </div>
-          </div>
-
-          <div>
-            <h6>Description</h6>
-          </div>
-          <div class="gf-form-group share-modal-options">
-            <div class="gf-form">
-              <textarea rows="3" class="gf-form-input width-27" ng-model="ctrl.annotation.text"></textarea>
-            </div>
-          </div>
-
-          <div class="gf-form-button-row">
-            <button class="btn gf-form-btn width-10 btn-success" ng-click="ctrl.addAnnotation()">
-              <i class="fa fa-pencil"></i>
-              Add Annotation
-            </button>
-          </div>
-        </div>
-
-        </div>
-      </div>
-    </div>
-  </div>
-</div>

+ 0 - 95
public/app/features/dashboard/partials/settings.html

@@ -1,95 +0,0 @@
-<div class="tabbed-view-header">
-	<h2 class="tabbed-view-title">
-		Settings
-	</h2>
-
-	<ul class="gf-tabs">
-		<li class="gf-tabs-item" ng-repeat="tab in ::['General', 'Links', 'Time picker']">
-			<a class="gf-tabs-link" ng-click="ctrl.editTab = $index" ng-class="{active: ctrl.editTab === $index}">
-				{{::tab}}
-			</a>
-		</li>
-	</ul>
-
-	<button class="tabbed-view-close-btn" ng-click="dismiss();">
-		<i class="fa fa-remove"></i>
-	</button>
-</div>
-
-<div class="tabbed-view-body">
-	<div ng-if="ctrl.editTab == 0">
-
-		<div class="gf-form-group section">
-      <h5 class="section-heading">Details</h5>
-			<div class="gf-form">
-				<label class="gf-form-label width-7">Name</label>
-				<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
-			</div>
-      <div class="gf-form">
-				<label class="gf-form-label width-7">Description</label>
-				<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
-			</div>
-      <div class="gf-form">
-				<label class="gf-form-label width-7">
-          Tags
-          <info-popover mode="right-normal">Press enter to add a tag</info-popover>
-        </label>
-				<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
-				</bootstrap-tagsinput>
-			</div>
-      <folder-picker ng-if="!ctrl.dashboard.meta.isFolder"
-                     initial-folder-id="ctrl.dashboard.folderId"
-										 on-change="ctrl.onFolderChange($folder)"
-										 label-class="width-7">
-			</folder-picker>
-		</div>
-
-    <div class="section">
-      <h5 class="section-heading">Options</h5>
-      <div class="gf-form-group">
-        <div class="gf-form">
-          <label class="gf-form-label width-11">Timezone</label>
-          <div class="gf-form-select-wrapper">
-            <select ng-model="ctrl.dashboard.timezone" class='gf-form-input' ng-options="f.value as f.text for f in [{value: '', text: 'Default'}, {value: 'browser', text: 'Local browser time'},{value: 'utc', text: 'UTC'}]" ng-change="timezoneChanged()"></select>
-          </div>
-        </div>
-        <gf-form-switch class="gf-form"
-                        label="Editable"
-                        tooltip="Uncheck, then save and reload to disable all dashboard editing"
-                        checked="ctrl.dashboard.editable"
-                        label-class="width-11">
-        </gf-form-switch>
-				<gf-form-switch class="gf-form"
-                        label="Hide Controls"
-                        tooltip="Hide row controls. Shortcut: CTRL+H or CMD+H"
-                        checked="ctrl.dashboard.hideControls"
-                        label-class="width-11">
-        </gf-form-switch>
-      </div>
-    </div>
-
-    <div class="section">
-      <h5 class="section-heading">Panel Options</h5>
-      <div class="gf-form">
-        <label class="gf-form-label width-11">
-          Graph Tooltip
-          <info-popover mode="right-normal">
-            Cycle between options using Shortcut: CTRL+O or CMD+O
-          </info-popover>
-        </label>
-        <div class="gf-form-select-wrapper">
-          <select ng-model="ctrl.dashboard.graphTooltip" class='gf-form-input' ng-options="f.value as f.text for f in [{value: 0, text: 'Default'}, {value: 1, text: 'Shared crosshair'},{value: 2, text: 'Shared Tooltip'}]"></select>
-        </div>
-      </div>
-    </div>
-	</div>
-
-	<div ng-if="editor.index == 1">
-		<dash-links-editor></dash-links-editor>
-	</div>
-
-	<div ng-if="editor.index == 2">
-		<gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
-	</div>
-
-</div>

+ 0 - 2
public/app/features/dashboard/save_as_modal.ts

@@ -1,5 +1,3 @@
-///<reference path="../../headers/common.d.ts" />
-
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 
 
 const  template = `
 const  template = `

+ 109 - 0
public/app/features/dashboard/settings/settings.html

@@ -0,0 +1,109 @@
+<aside class="dashboard-settings__aside">
+	<h2 class="dashboard-settings__aside-header">
+		<i class="fa fa-cog"></i>
+		Settings
+	</h2>
+
+	<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}" ng-repeat="section in ctrl.sections">
+    <i class="{{::section.icon}}"></i>
+		{{::section.title}}
+	</a>
+
+	<div class="dashboard-settings__aside-actions">
+		<button class="btn btn-inverse" ng-click="ctrl.openSaveAsModal()" ng-show="ctrl.canSaveAs">
+			<i class="fa fa-copy"></i>
+			Save As...
+		</button>
+
+		<button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-show="ctrl.canDelete">
+			<i class="fa fa-trash"></i>
+			Delete
+		</button>
+	</div>
+</aside>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'settings'">
+	<h3 class="dashboard-settings__header m-b-1">
+		General
+	</h3>
+
+	<div class="gf-form-group">
+		<div class="gf-form">
+			<label class="gf-form-label width-7">Name</label>
+			<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label width-7">Description</label>
+			<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label width-7">
+				Tags
+				<info-popover mode="right-normal">Press enter to add a tag</info-popover>
+			</label>
+			<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
+			</bootstrap-tagsinput>
+		</div>
+		<folder-picker initial-title="ctrl.dashboard.meta.folderTitle"
+								 initial-folder-id="ctrl.dashboard.folderId"
+				 on-change="ctrl.onFolderChange($folder)"
+		 label-class="width-7">
+		</folder-picker>
+		<gf-form-switch class="gf-form" label="Editable" tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="ctrl.dashboard.editable" label-class="width-7">
+		</gf-form-switch>
+	</div>
+
+	<gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
+
+	<h5 class="section-heading">Panel Options</h5>
+	<div class="gf-form">
+		<label class="gf-form-label width-11">
+			Graph Tooltip
+			<info-popover mode="right-normal">
+				Cycle between options using Shortcut: CTRL+O or CMD+O
+			</info-popover>
+		</label>
+		<div class="gf-form-select-wrapper">
+			<select ng-model="ctrl.dashboard.graphTooltip" class='gf-form-input' ng-options="f.value as f.text for f in [{value: 0, text: 'Default'}, {value: 1, text: 'Shared crosshair'},{value: 2, text: 'Shared Tooltip'}]"></select>
+		</div>
+	</div>
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'annotations'" ng-include="'public/app/features/annotations/partials/editor.html'">
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'templating'"  ng-include="'public/app/features/templating/partials/editor.html'">
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'links'" >
+	<dash-links-editor></dash-links-editor>
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'versions'" >
+	<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'view_json'" >
+	<h3 class="dashboard-settings__header">View JSON</h3>
+
+	<div class="gf-form">
+		<textarea class="gf-form-input" ng-model="ctrl.json" rows="30" spellcheck="false"></textarea>
+	</div>
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === '404'">
+  <h3 class="dashboard-settings__header">Settings view not found</h3>
+
+  <div>
+    <h5>The settings page could not be found or you do not have permission to access it</h5>
+  </div>
+</div>
+
+<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'make_editable'">
+  <h3 class="dashboard-settings__header">Make Editable</h3>
+
+  <button class="btn btn-success" ng-click="ctrl.makeEditable()">
+    Make Editable
+  </button>
+</div>
+

+ 156 - 0
public/app/features/dashboard/settings/settings.ts

@@ -0,0 +1,156 @@
+import { coreModule, appEvents, contextSrv } from 'app/core/core';
+import { DashboardModel } from '../dashboard_model';
+import $ from 'jquery';
+import _ from 'lodash';
+
+export class SettingsCtrl {
+  dashboard: DashboardModel;
+  isOpen: boolean;
+  viewId: string;
+  json: string;
+  alertCount: number;
+  canSaveAs: boolean;
+  canDelete: boolean;
+  sections: any[];
+
+  /** @ngInject */
+  constructor(private $scope, private $location, private $rootScope, private backendSrv, private dashboardSrv) {
+    // temp hack for annotations and variables editors
+    // that rely on inherited scope
+    $scope.dashboard = this.dashboard;
+
+    this.$scope.$on('$destroy', () => {
+      this.dashboard.updateSubmenuVisibility();
+      this.$rootScope.$broadcast('refresh');
+    });
+
+    this.canSaveAs = contextSrv.isEditor;
+    this.canDelete = this.dashboard.meta.canSave;
+
+    this.buildSectionList();
+    this.onRouteUpdated();
+
+    $rootScope.onAppEvent('$routeUpdate', this.onRouteUpdated.bind(this), $scope);
+  }
+
+  buildSectionList() {
+    this.sections = [];
+    if (this.dashboard.meta.canEdit) {
+      this.sections.push({ title: 'General', id: 'settings', icon: 'fa fa-fw fa-sliders' });
+      this.sections.push({ title: 'Annotations', id: 'annotations', icon: 'fa fa-fw fa-comment-o' });
+      this.sections.push({ title: 'Variables', id: 'templating', icon: 'fa fa-fw fa-dollar' });
+      this.sections.push({ title: 'Links', id: 'links', icon: 'fa fa-fw fa-external-link' });
+
+      if (this.dashboard.id) {
+        this.sections.push({ title: 'Versions', id: 'versions', icon: 'fa fa-fw fa-history' });
+      }
+    }
+
+    if (contextSrv.isEditor && !this.dashboard.editable) {
+      this.sections.push({ title: 'Make Editable', icon: 'fa fa-fw fa-edit', id: 'make_editable' });
+      this.viewId = 'make_editable';
+    }
+
+    this.sections.push({ title: 'View JSON', id: 'view_json', icon: 'fa fa-fw fa-code' });
+
+    const params = this.$location.search();
+    const url = this.$location.path();
+
+    for (let section of this.sections) {
+      const sectionParams = _.defaults({ editview: section.id }, params);
+      section.url = url + '?' + $.param(sectionParams);
+    }
+  }
+
+  onRouteUpdated() {
+    this.viewId = this.$location.search().editview;
+
+    if (this.viewId) {
+      this.json = JSON.stringify(this.dashboard.getSaveModelClone(), null, 2);
+    }
+
+    const currentSection = _.find(this.sections, { id: this.viewId });
+    if (!currentSection) {
+      this.sections.unshift({ title: 'Not found', id: '404', icon: 'fa fa-fw fa-warning' });
+      this.viewId = '404';
+      return;
+    }
+  }
+
+  openSaveAsModal() {
+    this.dashboardSrv.showSaveAsModal();
+  }
+
+  hideSettings() {
+    var urlParams = this.$location.search();
+    delete urlParams.editview;
+    setTimeout(() => {
+      this.$rootScope.$apply(() => {
+        this.$location.search(urlParams);
+      });
+    });
+  }
+
+  makeEditable() {
+    this.dashboard.editable = true;
+
+    return this.dashboardSrv.saveDashboard({ makeEditable: true, overwrite: false }).then(() => {
+      // force refresh whole page
+      window.location.href = window.location.href;
+    });
+  }
+
+  deleteDashboard() {
+    var confirmText = '';
+    var text2 = this.dashboard.title;
+
+    const alerts = _.sumBy(this.dashboard.panels, panel => {
+      return panel.alert ? 1 : 0;
+    });
+
+    if (alerts > 0) {
+      confirmText = 'DELETE';
+      text2 = `This dashboard contains ${alerts} alerts. Deleting this dashboard will also delete those alerts`;
+    }
+
+    appEvents.emit('confirm-modal', {
+      title: 'Delete',
+      text: 'Do you want to delete this dashboard?',
+      text2: text2,
+      icon: 'fa-trash',
+      confirmText: confirmText,
+      yesText: 'Delete',
+      onConfirm: () => {
+        this.dashboard.meta.canSave = false;
+        this.deleteDashboardConfirmed();
+      }
+    });
+  }
+
+  deleteDashboardConfirmed() {
+    this.backendSrv.delete('/api/dashboards/db/' + this.dashboard.meta.slug).then(() => {
+      appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']);
+      this.$location.url('/');
+    });
+  }
+
+  onFolderChange(folder) {
+    this.dashboard.folderId = folder.id;
+    this.dashboard.meta.folderId = folder.id;
+    this.dashboard.meta.folderTitle = folder.title;
+  }
+}
+
+export function dashboardSettings() {
+  return {
+    restrict: 'E',
+    templateUrl: 'public/app/features/dashboard/settings/settings.html',
+    controller: SettingsCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+    transclude: true,
+    scope: { dashboard: '=' },
+  };
+}
+
+coreModule.directive('dashboardSettings', dashboardSettings);

+ 1 - 1
public/app/features/dashboard/specs/save_as_modal.jest.ts

@@ -1,4 +1,4 @@
-import { SaveDashboardAsModalCtrl } from '../save_as_modal';
+import { SaveDashboardAsModalCtrl } from '../save_as_modal;
 import { describe, it, expect } from 'test/lib/common';
 import { describe, it, expect } from 'test/lib/common';
 
 
 describe('saving dashboard as', () => {
 describe('saving dashboard as', () => {

+ 0 - 1
public/app/features/dashboard/submenu/submenu.html

@@ -1,5 +1,4 @@
 <div class="submenu-controls">
 <div class="submenu-controls">
-
   <div ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
   <div ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
     <div class="gf-form">
     <div class="gf-form">
       <label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
       <label class="gf-form-label template-variable" ng-hide="variable.hide === 1">

+ 0 - 61
public/app/features/dashboard/timepicker/dropdown.html

@@ -1,61 +0,0 @@
-<div class="row pull-right">
-	<form name="timeForm" class="gf-timepicker-absolute-section">
-		<h3>Time range</h3>
-
-		<label class="small">From:</label>
-		<div class="gf-form-inline">
-			<div class="gf-form max-width-28">
-				<input type="text" class="gf-form-input input-large" ng-model="ctrl.editTimeRaw.from" input-datetime>
-			</div>
-			<div class="gf-form">
-				<button class="btn gf-form-btn btn-primary" type="button" ng-click="openFromPicker=!openFromPicker">
-					<i class="fa fa-calendar"></i>
-				</button>
-			</div>
-		</div>
-
-		<div ng-if="openFromPicker">
-			<datepicker ng-model="ctrl.absolute.fromJs" class="gf-timepicker-component" show-weeks="false" starting-day="ctrl.firstDayOfWeek" ng-change="ctrl.absoluteFromChanged()"></datepicker>
-		</div>
-
-
-		<label class="small">To:</label>
-		<div class="gf-form-inline">
-			<div class="gf-form max-width-28">
-				<input type="text" class="gf-form-input input-large" ng-model="ctrl.editTimeRaw.to" input-datetime>
-			</div>
-			<div class="gf-form">
-				<button class="btn gf-form-btn btn-primary" type="button" ng-click="openToPicker=!openToPicker">
-					<i class="fa fa-calendar"></i>
-				</button>
-			</div>
-		</div>
-
-		<div ng-if="openToPicker">
-			<datepicker ng-model="ctrl.absolute.toJs" class="gf-timepicker-component" show-weeks="false" starting-day="ctrl.firstDayOfWeek" ng-change="ctrl.absoluteToChanged()"></datepicker>
-		</div>
-
-		<label class="small">Refreshing every:</label>
-		<div class="gf-form-inline">
-			<div class="gf-form max-width-28">
-				<select ng-model="ctrl.refresh.value" class="gf-form-input input-medium" ng-options="f.value as f.text for f in ctrl.refresh.options"></select>
-			</div>
-			<div class="gf-form">
-				<button type="submit" class="btn gf-form-btn btn-secondary" ng-click="ctrl.applyCustom();" ng-disabled="!timeForm.$valid">Apply</button>
-			</div>
-		</div>
-
-	</form>
-
-	<div class="gf-timepicker-relative-section">
-		<h3>Quick ranges</h3>
-		<ul ng-repeat="group in ctrl.timeOptions">
-			<li bindonce ng-repeat='option in group' ng-class="{active: option.active}">
-				<a ng-click="ctrl.setRelativeFilter(option)" bo-text="option.display"></a>
-			</li>
-		</ul>
-	</div>
-
-</div>
-<div class="clearfix"></div>
-

+ 21 - 11
public/app/features/dashboard/timepicker/settings.html

@@ -1,14 +1,24 @@
 <div class="editor-row">
 <div class="editor-row">
+	<h5 class="section-heading">Time Options</h5>
+
   <div class="gf-form-group">
   <div class="gf-form-group">
-    <div class="gf-form">
-      <span class="gf-form-label width-10">Auto-refresh</span>
-      <input type="text" class="gf-form-input max-width-25" ng-model="ctrl.panel.refresh_intervals" array-join>
-    </div>
-    <div class="gf-form">
-      <span class="gf-form-label width-10">Now delay now-</span>
-      <input type="text" class="gf-form-input max-width-25" ng-model="ctrl.panel.nowDelay" placeholder="0m" valid-time-span bs-tooltip="'Enter 1m to ignore the last minute (because it can contain incomplete metrics)'"
-        data-placement="right">
-    </div>
-    <gf-form-switch class="gf-form" label="Hide time picker" checked="ctrl.panel.hidden" label-class="width-10"></gf-form-switch>
-  </div>
+		<div class="gf-form">
+			<label class="gf-form-label width-10">Timezone</label>
+			<div class="gf-form-select-wrapper">
+				<select ng-model="ctrl.dashboard.timezone" class='gf-form-input' ng-options="f.value as f.text for f in [{value: '', text: 'Default'}, {value: 'browser', text: 'Local browser time'},{value: 'utc', text: 'UTC'}]" ng-change="timezoneChanged()"></select>
+			</div>
+		</div>
+
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Auto-refresh</span>
+			<input type="text" class="gf-form-input max-width-25" ng-model="ctrl.panel.refresh_intervals" array-join>
+		</div>
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Now delay now-</span>
+			<input type="text" class="gf-form-input max-width-25" ng-model="ctrl.panel.nowDelay" placeholder="0m" valid-time-span bs-tooltip="'Enter 1m to ignore the last minute (because it can contain incomplete metrics)'"
+																																																												 data-placement="right">
+		</div>
+
+		<gf-form-switch class="gf-form" label="Hide time picker" checked="ctrl.panel.hidden" label-class="width-10"></gf-form-switch>
+	</div>
 </div>
 </div>

+ 59 - 0
public/app/features/dashboard/timepicker/timepicker.html

@@ -24,3 +24,62 @@
 		<i class="fa fa-refresh"></i>
 		<i class="fa fa-refresh"></i>
 	</button>
 	</button>
 </div>
 </div>
+
+<div ng-if="ctrl.isOpen" class="gf-timepicker-dropdown">
+  <form name="timeForm" class="gf-timepicker-absolute-section">
+    <h3 class="section-heading">Custom range</h3>
+
+    <label class="small">From:</label>
+    <div class="gf-form-inline">
+      <div class="gf-form max-width-28">
+        <input type="text" class="gf-form-input input-large" ng-model="ctrl.editTimeRaw.from" input-datetime>
+      </div>
+      <div class="gf-form">
+        <button class="btn gf-form-btn btn-primary" type="button" ng-click="openFromPicker=!openFromPicker">
+          <i class="fa fa-calendar"></i>
+        </button>
+      </div>
+    </div>
+
+    <div ng-if="openFromPicker">
+      <datepicker ng-model="ctrl.absolute.fromJs" class="gf-timepicker-component" show-weeks="false" starting-day="ctrl.firstDayOfWeek" ng-change="ctrl.absoluteFromChanged()"></datepicker>
+    </div>
+
+
+    <label class="small">To:</label>
+    <div class="gf-form-inline">
+      <div class="gf-form max-width-28">
+        <input type="text" class="gf-form-input input-large" ng-model="ctrl.editTimeRaw.to" input-datetime>
+      </div>
+      <div class="gf-form">
+        <button class="btn gf-form-btn btn-primary" type="button" ng-click="openToPicker=!openToPicker">
+          <i class="fa fa-calendar"></i>
+        </button>
+      </div>
+    </div>
+
+    <div ng-if="openToPicker">
+      <datepicker ng-model="ctrl.absolute.toJs" class="gf-timepicker-component" show-weeks="false" starting-day="ctrl.firstDayOfWeek" ng-change="ctrl.absoluteToChanged()"></datepicker>
+    </div>
+
+    <label class="small">Refreshing every:</label>
+    <div class="gf-form-inline">
+      <div class="gf-form max-width-28">
+        <select ng-model="ctrl.refresh.value" class="gf-form-input input-medium" ng-options="f.value as f.text for f in ctrl.refresh.options"></select>
+      </div>
+      <div class="gf-form">
+        <button type="submit" class="btn gf-form-btn btn-secondary" ng-click="ctrl.applyCustom();" ng-disabled="!timeForm.$valid">Apply</button>
+      </div>
+    </div>
+  </form>
+
+  <div class="gf-timepicker-relative-section">
+    <h3 class="section-heading">Quick ranges</h3>
+    <ul ng-repeat="group in ctrl.timeOptions">
+      <li bindonce ng-repeat='option in group' ng-class="{active: option.active}">
+        <a ng-click="ctrl.setRelativeFilter(option)" bo-text="option.display"></a>
+      </li>
+    </ul>
+  </div>
+</div>
+

+ 11 - 12
public/app/features/dashboard/timepicker/timepicker.ts

@@ -1,5 +1,3 @@
-///<reference path="../../../headers/common.d.ts" />
-
 import _ from 'lodash';
 import _ from 'lodash';
 import angular from 'angular';
 import angular from 'angular';
 import moment from 'moment';
 import moment from 'moment';
@@ -25,10 +23,12 @@ export class TimePickerCtrl {
   refresh: any;
   refresh: any;
   isUtc: boolean;
   isUtc: boolean;
   firstDayOfWeek: number;
   firstDayOfWeek: number;
+  closeDropdown: any;
+  isOpen: boolean;
 
 
   /** @ngInject */
   /** @ngInject */
   constructor(private $scope, private $rootScope, private timeSrv) {
   constructor(private $scope, private $rootScope, private timeSrv) {
-    $scope.ctrl = this;
+    this.$scope.ctrl = this;
 
 
     $rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope);
     $rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope);
     $rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope);
     $rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope);
@@ -96,6 +96,11 @@ export class TimePickerCtrl {
   }
   }
 
 
   openDropdown() {
   openDropdown() {
+    if (this.isOpen) {
+      this.isOpen = false;
+      return;
+    }
+
     this.onRefresh();
     this.onRefresh();
     this.editTimeRaw = this.timeRaw;
     this.editTimeRaw = this.timeRaw;
     this.timeOptions = rangeUtil.getRelativeTimesList(this.panel, this.rangeString);
     this.timeOptions = rangeUtil.getRelativeTimesList(this.panel, this.rangeString);
@@ -107,12 +112,7 @@ export class TimePickerCtrl {
     };
     };
 
 
     this.refresh.options.unshift({text: 'off'});
     this.refresh.options.unshift({text: 'off'});
-
-    this.$rootScope.appEvent('show-dash-editor', {
-      editview: 'timepicker',
-      scope: this.$scope,
-      cssClass: 'gf-timepicker-dropdown',
-    });
+    this.isOpen = true;
   }
   }
 
 
   applyCustom() {
   applyCustom() {
@@ -121,7 +121,7 @@ export class TimePickerCtrl {
     }
     }
 
 
     this.timeSrv.setTime(this.editTimeRaw);
     this.timeSrv.setTime(this.editTimeRaw);
-    this.$rootScope.appEvent('hide-dash-editor');
+    this.isOpen = false;
   }
   }
 
 
   absoluteFromChanged() {
   absoluteFromChanged() {
@@ -144,7 +144,7 @@ export class TimePickerCtrl {
     }
     }
 
 
     this.timeSrv.setTime(range);
     this.timeSrv.setTime(range);
-    this.$rootScope.appEvent('hide-dash-editor');
+    this.isOpen = false;
   }
   }
 
 
 }
 }
@@ -175,7 +175,6 @@ export function timePickerDirective() {
   };
   };
 }
 }
 
 
-
 angular.module('grafana.directives').directive('gfTimePickerSettings', settingsDirective);
 angular.module('grafana.directives').directive('gfTimePickerSettings', settingsDirective);
 angular.module('grafana.directives').directive('gfTimePicker', timePickerDirective);
 angular.module('grafana.directives').directive('gfTimePicker', timePickerDirective);
 
 

+ 57 - 60
public/app/features/dashlinks/editor.html

@@ -1,80 +1,77 @@
-<div class="editor-row">
-	<h5 class="section-heading">Links and Dash Navigation</h5>
+<h3 class="dashboard-settings__header">
+	Dashboard Links
+</h3>
 
 
-	<div ng-repeat="link in dashboard.links">
-
-		<div class="gf-form-group gf-form-inline">
-			<div class="section">
-				<div class="gf-form">
-					<span class="gf-form-label width-8">Type</span>
-					<div class="gf-form-select-wrapper width-10">
-						<select class="gf-form-input" ng-model="link.type" ng-options="f for f in ['dashboards','link']" ng-change="updated()"></select>
-					</div>
+<div ng-repeat="link in dashboard.links">
+	<div class="gf-form-group gf-form-inline">
+		<div class="section">
+			<div class="gf-form">
+				<span class="gf-form-label width-8">Type</span>
+				<div class="gf-form-select-wrapper width-10">
+					<select class="gf-form-input" ng-model="link.type" ng-options="f for f in ['dashboards','link']" ng-change="updated()"></select>
 				</div>
 				</div>
-				<div class="gf-form" ng-show="link.type === 'dashboards'">
-					<span class="gf-form-label width-8">With tags</span>
-					<bootstrap-tagsinput ng-model="link.tags" tagclass="label label-tag" placeholder="add tags" style="margin-right: .25rem"></bootstrap-tagsinput>
+			</div>
+			<div class="gf-form" ng-show="link.type === 'dashboards'">
+				<span class="gf-form-label width-8">With tags</span>
+				<bootstrap-tagsinput ng-model="link.tags" tagclass="label label-tag" placeholder="add tags" style="margin-right: .25rem"></bootstrap-tagsinput>
+			</div>
+			<gf-form-switch ng-show="link.type === 'dashboards'" class="gf-form" label="As dropdown" checked="link.asDropdown" switch-class="max-width-4" label-class="width-8" on-change="updated()"></gf-form-switch>
+			<div class="gf-form" ng-show="link.type === 'dashboards' && link.asDropdown">
+				<span class="gf-form-label width-8">Title</span>
+				<input type="text" ng-model="link.title" class="gf-form-input max-width-10" ng-model-onblur ng-change="updated()">
+			</div>
+			<div ng-show="link.type === 'link'">
+				<div class="gf-form">
+					<li class="gf-form-label width-8">Url</li>
+					<input type="text" ng-model="link.url" class="gf-form-input width-20" ng-model-onblur ng-change="updated()">
 				</div>
 				</div>
-				<gf-form-switch ng-show="link.type === 'dashboards'" class="gf-form" label="As dropdown" checked="link.asDropdown" switch-class="max-width-4" label-class="width-8" on-change="updated()"></gf-form-switch>
-				<div class="gf-form" ng-show="link.type === 'dashboards' && link.asDropdown">
+
+				<div class="gf-form">
 					<span class="gf-form-label width-8">Title</span>
 					<span class="gf-form-label width-8">Title</span>
-					<input type="text" ng-model="link.title" class="gf-form-input max-width-10" ng-model-onblur ng-change="updated()">
+					<input type="text" ng-model="link.title" class="gf-form-input width-20" ng-model-onblur ng-change="updated()">
 				</div>
 				</div>
-				<div ng-show="link.type === 'link'">
-					<div class="gf-form">
-						<li class="gf-form-label width-8">Url</li>
-						<input type="text" ng-model="link.url" class="gf-form-input width-20" ng-model-onblur ng-change="updated()">
-					</div>
-
-					<div class="gf-form">
-						<span class="gf-form-label width-8">Title</span>
-						<input type="text" ng-model="link.title" class="gf-form-input width-20" ng-model-onblur ng-change="updated()">
-					</div>
 
 
-					<div class="gf-form">
-						<span class="gf-form-label width-8">Tooltip</span>
-						<input type="text" ng-model="link.tooltip" class="gf-form-input width-20" placeholder="Open dashboard" ng-model-onblur ng-change="updated()">
-					</div>
-
-					<div class="gf-form">
-						<span class="gf-form-label width-8">Icon</span>
-						<div class="gf-form-select-wrapper width-20">
-								<select class="gf-form-input" ng-model="link.icon" ng-options="k as k for (k, v) in iconMap" ng-change="updated()"></select>
-						</div>
-					</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-8">Tooltip</span>
+					<input type="text" ng-model="link.tooltip" class="gf-form-input width-20" placeholder="Open dashboard" ng-model-onblur ng-change="updated()">
 				</div>
 				</div>
-			</div>
 
 
-			<div class="section gf-form-inline"  style="display: flex">
-				<div>
-					<div class="gf-form">
-						<span class="gf-form-label width-6">Include</span>
+				<div class="gf-form">
+					<span class="gf-form-label width-8">Icon</span>
+					<div class="gf-form-select-wrapper width-20">
+						<select class="gf-form-input" ng-model="link.icon" ng-options="k as k for (k, v) in iconMap" ng-change="updated()"></select>
 					</div>
 					</div>
 				</div>
 				</div>
-				<div>
-					<gf-form-switch class="gf-form" label="Time range" checked="link.keepTime" switch-class="max-width-6" label-class="width-9"></gf-form-switch>
-					<gf-form-switch class="gf-form" label="Variable values" checked="link.includeVars" switch-class="max-width-6" label-class="width-9"></gf-form-switch>
-					<gf-form-switch class="gf-form" label="Open in new tab" checked="link.targetBlank" switch-class="max-width-6" label-class="width-9"></gf-form-switch>
-				</div>
 			</div>
 			</div>
+		</div>
 
 
-			<div style="display:flex; flex-direction:column; justify-content:flex-start">
-				<div class="gf-form">
-					<button class="btn btn-inverse gf-form-btn width-4" ng-click="deleteLink($index)">
-						<i class="fa fa-trash"></i>
-					</button>
-				</div>
-				<div class="gf-form">
-					<button class="btn btn-inverse gf-form-btn width-4" ng-click="moveLink($index, -1)" ng-hide="$first"><i class="fa fa-arrow-up"></i></button>
-				</div>
+		<div class="section gf-form-inline"  style="display: flex">
+			<div>
 				<div class="gf-form">
 				<div class="gf-form">
-					<button class="btn btn-inverse gf-form-btn width-4" ng-click="moveLink($index, 1)" ng-hide="$last"><i class="fa fa-arrow-down"></i></button>
+					<span class="gf-form-label width-6">Include</span>
 				</div>
 				</div>
 			</div>
 			</div>
+			<div>
+				<gf-form-switch class="gf-form" label="Time range" checked="link.keepTime" switch-class="max-width-6" label-class="width-9"></gf-form-switch>
+				<gf-form-switch class="gf-form" label="Variable values" checked="link.includeVars" switch-class="max-width-6" label-class="width-9"></gf-form-switch>
+				<gf-form-switch class="gf-form" label="Open in new tab" checked="link.targetBlank" switch-class="max-width-6" label-class="width-9"></gf-form-switch>
+			</div>
+		</div>
+
+		<div style="display:flex; flex-direction:column; justify-content:flex-start">
+			<div class="gf-form">
+				<button class="btn btn-inverse gf-form-btn width-4" ng-click="deleteLink($index)">
+					<i class="fa fa-trash"></i>
+				</button>
+			</div>
+			<div class="gf-form">
+				<button class="btn btn-inverse gf-form-btn width-4" ng-click="moveLink($index, -1)" ng-hide="$first"><i class="fa fa-arrow-up"></i></button>
+			</div>
+			<div class="gf-form">
+				<button class="btn btn-inverse gf-form-btn width-4" ng-click="moveLink($index, 1)" ng-hide="$last"><i class="fa fa-arrow-down"></i></button>
+			</div>
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>
 
 
-
-
 <button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
 <button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>

+ 11 - 4
public/app/features/panel/panel_ctrl.ts

@@ -134,20 +134,27 @@ export class PanelCtrl {
   getMenu() {
   getMenu() {
     let menu = [];
     let menu = [];
     menu.push({text: 'View', click: 'ctrl.viewPanel();', icon: "fa fa-fw fa-eye", shortcut: "v"});
     menu.push({text: 'View', click: 'ctrl.viewPanel();', icon: "fa fa-fw fa-eye", shortcut: "v"});
-    menu.push({text: 'Edit', click: 'ctrl.editPanel();', role: 'Editor', icon: "fa fa-fw fa-edit", shortcut: "e"});
+
+    if (this.dashboard.meta.canEdit) {
+      menu.push({text: 'Edit', click: 'ctrl.editPanel();', role: 'Editor', icon: "fa fa-fw fa-edit", shortcut: "e"});
+    }
+
     menu.push({text: 'Share', click: 'ctrl.sharePanel();', icon: "fa fa-fw fa-share", shortcut: "p s"});
     menu.push({text: 'Share', click: 'ctrl.sharePanel();', icon: "fa fa-fw fa-share", shortcut: "p s"});
 
 
     let extendedMenu = this.getExtendedMenu();
     let extendedMenu = this.getExtendedMenu();
     menu.push({text: 'More ...', click: 'ctrl.removePanel();', icon: "fa fa-fw fa-cube", submenu: extendedMenu});
     menu.push({text: 'More ...', click: 'ctrl.removePanel();', icon: "fa fa-fw fa-cube", submenu: extendedMenu});
 
 
-    menu.push({divider: true, role: 'Editor'});
-    menu.push({text: 'Remove', click: 'ctrl.removePanel();', role: 'Editor', icon: "fa fa-fw fa-trash", shortcut: "p r"});
+    if (this.dashboard.meta.canEdit) {
+      menu.push({divider: true, role: 'Editor'});
+      menu.push({text: 'Remove', click: 'ctrl.removePanel();', role: 'Editor', icon: "fa fa-fw fa-trash", shortcut: "p r"});
+    }
+
     return menu;
     return menu;
   }
   }
 
 
   getExtendedMenu() {
   getExtendedMenu() {
     let menu = [];
     let menu = [];
-    if (!this.fullscreen) {
+    if (!this.fullscreen && this.dashboard.meta.canEdit) {
       menu.push({ text: 'Duplicate', click: 'ctrl.duplicate()', role: 'Editor' });
       menu.push({ text: 'Duplicate', click: 'ctrl.duplicate()', role: 'Editor' });
     }
     }
     menu.push({text: 'Panel JSON', click: 'ctrl.editPanelJson(); dismiss();' });
     menu.push({text: 'Panel JSON', click: 'ctrl.editPanelJson(); dismiss();' });

+ 4 - 2
public/app/features/templating/editor_ctrl.ts

@@ -1,5 +1,3 @@
-///<reference path="../../headers/common.d.ts" />
-
 import _ from 'lodash';
 import _ from 'lodash';
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 import {variableTypes} from './variable';
 import {variableTypes} from './variable';
@@ -45,6 +43,10 @@ export class VariableEditorCtrl {
       });
       });
     };
     };
 
 
+    $scope.setMode = function(mode) {
+      $scope.mode = mode;
+    };
+
     $scope.add = function() {
     $scope.add = function() {
       if ($scope.isValid()) {
       if ($scope.isValid()) {
         variableSrv.addVariable($scope.current);
         variableSrv.addVariable($scope.current);

+ 242 - 265
public/app/features/templating/partials/editor.html

@@ -1,45 +1,43 @@
 <div ng-controller="VariableEditorCtrl" ng-init="init()">
 <div ng-controller="VariableEditorCtrl" ng-init="init()">
-  <div class="tabbed-view-header">
-    <h2 class="tabbed-view-title">
-      Templating
-    </h2>
 
 
-    <ul class="gf-tabs">
-      <li class="gf-tabs-item" >
-        <a class="gf-tabs-link" ng-click="mode = 'list';" ng-class="{active: mode === 'list'}">
-          Variables
-        </a>
-      </li>
-      <li class="gf-tabs-item" ng-show="mode === 'edit'">
-        <a class="gf-tabs-link" ng-class="{active: mode === 'edit'}">
-          Edit
-        </a>
-      </li>
-      <li class="gf-tabs-item" ng-show="mode === 'new'">
-        <span class="active gf-tabs-link">New</span>
-      </li>
-			<li class="gf-tabs-item" >
-        <a class="gf-tabs-link" ng-click="mode = 'help';" ng-class="{active: mode === 'help'}">
-          Help
-        </a>
-      </li>
-    </ul>
+	<h3 class="dashboard-settings__header">
+		<a ng-click="setMode('list')">Variables</a>
+		<span ng-show="mode === 'new'">&gt; New</span>
+		<span ng-show="mode === 'edit'">&gt; Edit</span>
+	</h3>
 
 
-    <button class="tabbed-view-close-btn" ng-click="dismiss();">
-      <i class="fa fa-remove"></i>
-    </button>
-  </div>
+	<div ng-if="mode === 'list'">
 
 
-  <div class="tabbed-view-body">
+		<div ng-if="variables.length === 0">
+			<div class="empty-list-cta">
+				<div class="empty-list-cta__title">There are no variables added yet</div>
+				<a ng-click="setMode('new')" class="empty-list-cta__button btn btn-xlarge btn-success">
+					<i class="gicon gicon-dashboard-new"></i>
+					Add variable
+				</a>
+				<div class="grafana-info-box">
+					<h5>What does variables do?</h5>
+					<p>Variables enables more interactive and dynamic dashboards. Instead of hard-coding things like server or sensor names
+					in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of
+					the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
 
 
-    <div ng-if="mode === 'list'">
-      <div ng-if="variables.length === 0">
-        <em>No template variables defined</em>
-        <br /> <br />
+					Checkout the
+					<a class="external-link" href="http://docs.grafana.org/reference/templating/" target="_blank">
+						Templating documentation
+					</a> for more information.
+				</div>
 			</div>
 			</div>
-      <table class="filter-table filter-table--hover">
-      	<thead>
-      		<tr>
+		</div>
+
+		<div ng-if="variables.length">
+			<div class="page-action-bar">
+				<div class="page-action-bar__spacer"></div>
+				<a type="button" class="btn btn-success" ng-click="setMode('new');"><i class="fa fa-plus" ></i> New</a>
+			</div>
+
+			<table class="filter-table filter-table--hover">
+				<thead>
+					<tr>
 						<th>Variable</th>
 						<th>Variable</th>
 						<th>Definition</th>
 						<th>Definition</th>
 						<th colspan="5"></th>
 						<th colspan="5"></th>
@@ -55,7 +53,6 @@
 						<td style="max-width: 200px;" ng-click="edit(variable)" class="pointer max-width">
 						<td style="max-width: 200px;" ng-click="edit(variable)" class="pointer max-width">
 							{{variable.query}}
 							{{variable.query}}
 						</td>
 						</td>
-
 						<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
 						<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
 						<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
 						<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
 						<td style="width: 1%">
 						<td style="width: 1%">
@@ -70,256 +67,236 @@
 						</td>
 						</td>
 					</tr>
 					</tr>
 				</tbody>
 				</tbody>
-      </table>
-    </div>
-
-    <div ng-show="mode === 'help'">
-			<div class="grafana-info-box col-lg-8">
-				<h5>What does templating do?</h5>
-				<p>Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application
-				and sensor name in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of
-				the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
-				<br>
-				<br>
-
-				Checkout the <a class="external-link" target="_blank" href="http://docs.grafana.org/reference/templating/">Templating documentation</a> for more information.
-				</p>
-			</div>
+			</table>
 		</div>
 		</div>
+	</div>
 
 
-    <div class="gf-form" ng-show="mode === 'list'">
-      <div class="gf-form-button-row">
-        <a type="button" class="btn gf-form-button btn-success" ng-click="mode = 'new';"><i class="fa fa-plus" ></i>&nbsp;&nbsp;New</a>
-      </div>
-    </div>
-
-    <form ng-if="mode === 'edit' || mode === 'new'" name="ctrl.form">
-      <h5 class="section-heading">Variable</h5>
-      <div class="gf-form-group">
-				<div class="gf-form-inline">
-          <div class="gf-form max-width-19">
-            <span class="gf-form-label width-6">Name</span>
-            <input type="text" class="gf-form-input" name="name" placeholder="name" ng-model='current.name' required ng-pattern="namePattern"></input>
-          </div>
-          <div class="gf-form max-width-19">
-            <span class="gf-form-label width-6">
-              Type
-              <info-popover mode="right-normal">
-                {{variableTypes[current.type].description}}
-              </info-popover>
-            </span>
-            <div class="gf-form-select-wrapper max-width-17">
-              <select class="gf-form-input" ng-model="current.type" ng-options="k as v.name for (k, v) in variableTypes" ng-change="typeChanged()"></select>
-            </div>
-          </div>
-        </div>
+	<form ng-if="mode === 'edit' || mode === 'new'" name="ctrl.form">
+		<h5 class="section-heading">General</h5>
+		<div class="gf-form-group">
+			<div class="gf-form-inline">
+				<div class="gf-form max-width-19">
+					<span class="gf-form-label width-6">Name</span>
+					<input type="text" class="gf-form-input" name="name" placeholder="name" ng-model='current.name' required ng-pattern="namePattern"></input>
+				</div>
+				<div class="gf-form max-width-19">
+					<span class="gf-form-label width-6">
+						Type
+						<info-popover mode="right-normal">
+							{{variableTypes[current.type].description}}
+						</info-popover>
+					</span>
+					<div class="gf-form-select-wrapper max-width-17">
+						<select class="gf-form-input" ng-model="current.type" ng-options="k as v.name for (k, v) in variableTypes" ng-change="typeChanged()"></select>
+					</div>
+				</div>
+			</div>
 
 
-				<div class="gf-form" ng-show="ctrl.form.name.$error.pattern">
-          <span class="gf-form-label gf-form-label--error">Template names cannot begin with '__' that's reserved for Grafanas global variables</span>
-        </div>
+			<div class="gf-form" ng-show="ctrl.form.name.$error.pattern">
+				<span class="gf-form-label gf-form-label--error">Template names cannot begin with '__' that's reserved for Grafanas global variables</span>
+			</div>
 
 
-        <div class="gf-form-inline">
-          <div class="gf-form max-width-19">
-            <span class="gf-form-label width-6">Label</span>
-            <input type="text" class="gf-form-input" ng-model='current.label' placeholder="optional display name"></input>
-          </div>
-          <div class="gf-form max-width-19">
-            <span class="gf-form-label width-6">Hide</span>
-            <div class="gf-form-select-wrapper max-width-15">
-              <select class="gf-form-input" ng-model="current.hide" ng-options="f.value as f.text for f in hideOptions"></select>
-            </div>
-          </div>
-        </div>
-      </div>
+			<div class="gf-form-inline">
+				<div class="gf-form max-width-19">
+					<span class="gf-form-label width-6">Label</span>
+					<input type="text" class="gf-form-input" ng-model='current.label' placeholder="optional display name"></input>
+				</div>
+				<div class="gf-form max-width-19">
+					<span class="gf-form-label width-6">Hide</span>
+					<div class="gf-form-select-wrapper max-width-15">
+						<select class="gf-form-input" ng-model="current.hide" ng-options="f.value as f.text for f in hideOptions"></select>
+					</div>
+				</div>
+			</div>
+		</div>
 
 
-      <div ng-if="current.type === 'interval'" class="gf-form-group">
-        <h5 class="section-heading">Interval Options</h5>
+		<div ng-if="current.type === 'interval'" class="gf-form-group">
+			<h5 class="section-heading">Interval Options</h5>
 
 
-        <div class="gf-form">
-          <span class="gf-form-label width-9">Values</span>
-          <input type="text" class="gf-form-input" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()" required></input>
-        </div>
+			<div class="gf-form">
+				<span class="gf-form-label width-9">Values</span>
+				<input type="text" class="gf-form-input" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()" required></input>
+			</div>
 
 
-        <div class="gf-form-inline">
-          <gf-form-switch class="gf-form" label="Auto Option" label-class="width-9" checked="current.auto" on-change="runQuery()">
-          </gf-form-switch>
+			<div class="gf-form-inline">
+				<gf-form-switch class="gf-form" label="Auto Option" label-class="width-9" checked="current.auto" on-change="runQuery()">
+				</gf-form-switch>
 
 
-          <div class="gf-form">
-            <span class="gf-form-label width-9" ng-show="current.auto">
-              Step count <tip>How many times should the current time range be divided to calculate the value</tip>
-            </span>
-            <div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
-              <select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [1,2,3,4,5,10,20,30,40,50,100,200,300,400,500]" ng-change="runQuery()"></select>
-            </div>
-          </div>
-          <div class="gf-form">
-            <span class="gf-form-label" ng-show="current.auto">
-              Min interval <tip>The calculated value will not go below this threshold</tip>
-            </span>
-            <input type="text" class="gf-form-input max-width-10" ng-show="current.auto" ng-model="current.auto_min" ng-change="runQuery()" placeholder="10s"></input>
-          </div>
-        </div>
-      </div>
+				<div class="gf-form">
+					<span class="gf-form-label width-9" ng-show="current.auto">
+						Step count <tip>How many times should the current time range be divided to calculate the value</tip>
+					</span>
+					<div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
+						<select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [1,2,3,4,5,10,20,30,40,50,100,200,300,400,500]" ng-change="runQuery()"></select>
+					</div>
+				</div>
+				<div class="gf-form">
+					<span class="gf-form-label" ng-show="current.auto">
+						Min interval <tip>The calculated value will not go below this threshold</tip>
+					</span>
+					<input type="text" class="gf-form-input max-width-10" ng-show="current.auto" ng-model="current.auto_min" ng-change="runQuery()" placeholder="10s"></input>
+				</div>
+			</div>
+		</div>
 
 
-      <div ng-if="current.type === 'custom'" class="gf-form-group">
-        <h5 class="section-heading">Custom Options</h5>
-        <div class="gf-form">
-          <span class="gf-form-label width-14">Values separated by comma</span>
-          <input type="text" class="gf-form-input" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue" required></input>
-        </div>
-      </div>
+		<div ng-if="current.type === 'custom'" class="gf-form-group">
+			<h5 class="section-heading">Custom Options</h5>
+			<div class="gf-form">
+				<span class="gf-form-label width-14">Values separated by comma</span>
+				<input type="text" class="gf-form-input" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue" required></input>
+			</div>
+		</div>
 
 
-      <div ng-if="current.type === 'constant'" class="gf-form-group">
-        <h5 class="section-heading">Constant options</h5>
-        <div class="gf-form">
-          <span class="gf-form-label">Value</span>
-          <input type="text" class="gf-form-input" ng-model='current.query' ng-blur="runQuery()" placeholder="your metric prefix"></input>
-        </div>
-      </div>
+		<div ng-if="current.type === 'constant'" class="gf-form-group">
+			<h5 class="section-heading">Constant options</h5>
+			<div class="gf-form">
+				<span class="gf-form-label">Value</span>
+				<input type="text" class="gf-form-input" ng-model='current.query' ng-blur="runQuery()" placeholder="your metric prefix"></input>
+			</div>
+		</div>
 
 
-      <div ng-if="current.type === 'query'" class="gf-form-group">
-        <h5 class="section-heading">Query Options</h5>
+		<div ng-if="current.type === 'query'" class="gf-form-group">
+			<h5 class="section-heading">Query Options</h5>
 
 
-        <div class="gf-form-inline">
-          <div class="gf-form max-width-21">
-            <span class="gf-form-label width-7">Data source</span>
-            <div class="gf-form-select-wrapper max-width-14">
-              <select class="gf-form-input" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources" required>
-                <option value="" ng-if="false"></option>
-              </select>
-            </div>
-          </div>
-          <div class="gf-form max-width-21">
-            <span class="gf-form-label width-7">
-              Refresh
-              <info-popover mode="right-normal">
-                When to update the values of this variable.
-              </info-popover>
-            </span>
-            <div class="gf-form-select-wrapper max-width-14">
-              <select class="gf-form-input" ng-model="current.refresh" ng-options="f.value as f.text for f in refreshOptions"></select>
-            </div>
-          </div>
-        </div>
-        <div class="gf-form">
-          <span class="gf-form-label width-7">Query</span>
-          <input type="text" class="gf-form-input" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()" required></input>
-        </div>
-        <div class="gf-form">
-          <span class="gf-form-label width-7">
-            Regex
-            <info-popover mode="right-normal">
-              Optional, if you want to extract part of a series name or metric node segment.
-            </info-popover>
-          </span>
-          <input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
-        </div>
-        <div class="gf-form max-width-21">
-          <span class="gf-form-label width-7">
-            Sort
-            <info-popover mode="right-normal">
-              How to sort the values of this variable.
-            </info-popover>
-          </span>
-          <div class="gf-form-select-wrapper max-width-14">
-            <select class="gf-form-input" ng-model="current.sort" ng-options="f.value as f.text for f in sortOptions" ng-change="runQuery()"></select>
-          </div>
-        </div>
-      </div>
+			<div class="gf-form-inline">
+				<div class="gf-form max-width-21">
+					<span class="gf-form-label width-7">Data source</span>
+					<div class="gf-form-select-wrapper max-width-14">
+						<select class="gf-form-input" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources" required>
+							<option value="" ng-if="false"></option>
+						</select>
+					</div>
+				</div>
+				<div class="gf-form max-width-21">
+					<span class="gf-form-label width-7">
+						Refresh
+						<info-popover mode="right-normal">
+							When to update the values of this variable.
+						</info-popover>
+					</span>
+					<div class="gf-form-select-wrapper max-width-14">
+						<select class="gf-form-input" ng-model="current.refresh" ng-options="f.value as f.text for f in refreshOptions"></select>
+					</div>
+				</div>
+			</div>
+			<div class="gf-form">
+				<span class="gf-form-label width-7">Query</span>
+				<input type="text" class="gf-form-input" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()" required></input>
+			</div>
+			<div class="gf-form">
+				<span class="gf-form-label width-7">
+					Regex
+					<info-popover mode="right-normal">
+						Optional, if you want to extract part of a series name or metric node segment.
+					</info-popover>
+				</span>
+				<input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
+			</div>
+			<div class="gf-form max-width-21">
+				<span class="gf-form-label width-7">
+					Sort
+					<info-popover mode="right-normal">
+						How to sort the values of this variable.
+					</info-popover>
+				</span>
+				<div class="gf-form-select-wrapper max-width-14">
+					<select class="gf-form-input" ng-model="current.sort" ng-options="f.value as f.text for f in sortOptions" ng-change="runQuery()"></select>
+				</div>
+			</div>
+		</div>
 
 
-      <div ng-show="current.type === 'datasource'" class="gf-form-group">
-        <h5 class="section-heading">Data source options</h5>
+		<div ng-show="current.type === 'datasource'" class="gf-form-group">
+			<h5 class="section-heading">Data source options</h5>
 
 
-        <div class="gf-form">
-          <label class="gf-form-label width-12">Type</label>
-          <div class="gf-form-select-wrapper max-width-18">
-            <select class="gf-form-input" ng-model="current.query" ng-options="f.value as f.text for f in datasourceTypes" ng-change="runQuery()"></select>
-          </div>
-        </div>
+			<div class="gf-form">
+				<label class="gf-form-label width-12">Type</label>
+				<div class="gf-form-select-wrapper max-width-18">
+					<select class="gf-form-input" ng-model="current.query" ng-options="f.value as f.text for f in datasourceTypes" ng-change="runQuery()"></select>
+				</div>
+			</div>
 
 
-        <div class="gf-form">
-          <label class="gf-form-label width-12">
-            Instance name filter
-            <info-popover mode="right-normal">
-              Regex filter for which data source instances to choose from in
-              the variable value dropdown. Leave empty for all.
-              <br><br>
-              Example: <code>/^prod/</code>
+			<div class="gf-form">
+				<label class="gf-form-label width-12">
+					Instance name filter
+					<info-popover mode="right-normal">
+						Regex filter for which data source instances to choose from in
+						the variable value dropdown. Leave empty for all.
+						<br><br>
+						Example: <code>/^prod/</code>
 
 
-            </info-popover>
-          </label>
-          <input type="text" class="gf-form-input max-width-18" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
-        </div>
-      </div>
+					</info-popover>
+				</label>
+				<input type="text" class="gf-form-input max-width-18" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
+			</div>
+		</div>
 
 
-      <div ng-if="current.type === 'adhoc'" class="gf-form-group">
-        <h5 class="section-heading">Options</h5>
-        <div class="gf-form max-width-21">
-          <span class="gf-form-label width-8">Data source</span>
-          <div class="gf-form-select-wrapper max-width-14">
-            <select class="gf-form-input" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources" required ng-change="validate()">
-              <option value="" ng-if="false"></option>
-            </select>
-          </div>
-        </div>
-      </div>
+		<div ng-if="current.type === 'adhoc'" class="gf-form-group">
+			<h5 class="section-heading">Options</h5>
+			<div class="gf-form max-width-21">
+				<span class="gf-form-label width-8">Data source</span>
+				<div class="gf-form-select-wrapper max-width-14">
+					<select class="gf-form-input" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources" required ng-change="validate()">
+						<option value="" ng-if="false"></option>
+					</select>
+				</div>
+			</div>
+		</div>
 
 
-      <div class="section gf-form-group" ng-show="variableTypes[current.type].supportsMulti">
-        <h5 class="section-heading">Selection Options</h5>
-        <div class="section">
-          <gf-form-switch class="gf-form"
-                          label="Multi-value"
-                          label-class="width-10"
-                          tooltip="Enables multiple values to be selected at the same time"
-                          checked="current.multi"
-                          on-change="runQuery()">
-          </gf-form-switch>
-          <gf-form-switch class="gf-form"
-                          label="Include All option"
-                          label-class="width-10"
-                          checked="current.includeAll"
-                          on-change="runQuery()">
-          </gf-form-switch>
-        </div>
-        <div class="gf-form" ng-if="current.includeAll">
-          <span class="gf-form-label width-10">Custom all value</span>
-          <input type="text" class="gf-form-input max-width-15" ng-model='current.allValue' placeholder="blank = auto"></input>
-        </div>
-      </div>
+		<div class="section gf-form-group" ng-show="variableTypes[current.type].supportsMulti">
+			<h5 class="section-heading">Selection Options</h5>
+			<div class="section">
+				<gf-form-switch class="gf-form"
+										label="Multi-value"
+					label-class="width-10"
+		 tooltip="Enables multiple values to be selected at the same time"
+	 checked="current.multi"
+	on-change="runQuery()">
+				</gf-form-switch>
+				<gf-form-switch class="gf-form"
+										label="Include All option"
+					label-class="width-10"
+		 checked="current.includeAll"
+	 on-change="runQuery()">
+				</gf-form-switch>
+			</div>
+			<div class="gf-form" ng-if="current.includeAll">
+				<span class="gf-form-label width-10">Custom all value</span>
+				<input type="text" class="gf-form-input max-width-15" ng-model='current.allValue' placeholder="blank = auto"></input>
+			</div>
+		</div>
 
 
-      <div class="gf-form-group" ng-if="current.type === 'query'">
-        <h5>Value groups/tags (Experimental feature)</h5>
-        <gf-form-switch class="gf-form" label="Enabled" label-class="width-10" checked="current.useTags" on-change="runQuery()">
-        </gf-form-switch>
-        <div class="gf-form last" ng-if="current.useTags">
-          <span class="gf-form-label width-10">Tags query</span>
-          <input type="text" class="gf-form-input" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
-        </div>
-        <div class="gf-form" ng-if="current.useTags">
-          <li class="gf-form-label width-10">Tag values query</li>
-          <input type="text" class="gf-form-input" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
-        </div>
-      </div>
+		<div class="gf-form-group" ng-if="current.type === 'query'">
+			<h5>Value groups/tags (Experimental feature)</h5>
+			<gf-form-switch class="gf-form" label="Enabled" label-class="width-10" checked="current.useTags" on-change="runQuery()">
+			</gf-form-switch>
+			<div class="gf-form last" ng-if="current.useTags">
+				<span class="gf-form-label width-10">Tags query</span>
+				<input type="text" class="gf-form-input" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
+			</div>
+			<div class="gf-form" ng-if="current.useTags">
+				<li class="gf-form-label width-10">Tag values query</li>
+				<input type="text" class="gf-form-input" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
+			</div>
+		</div>
 
 
-      <div class="gf-form-group" ng-show="current.options.length">
-        <h5>Preview of values (shows max 20)</h5>
-        <div class="gf-form-inline">
-          <div class="gf-form" ng-repeat="option in current.options | limitTo: 20">
-            <span class="gf-form-label">{{option.text}}</span>
-          </div>
-        </div>
-      </div>
+		<div class="gf-form-group" ng-show="current.options.length">
+			<h5>Preview of values (shows max 20)</h5>
+			<div class="gf-form-inline">
+				<div class="gf-form" ng-repeat="option in current.options | limitTo: 20">
+					<span class="gf-form-label">{{option.text}}</span>
+				</div>
+			</div>
+		</div>
 
 
-      <div class="alert alert-info gf-form-group" ng-if="infoText">
-        {{infoText}}
-      </div>
+		<div class="alert alert-info gf-form-group" ng-if="infoText">
+			{{infoText}}
+		</div>
 
 
-      <div class="gf-form-button-row p-y-0">
-        <button type="submit" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
-        <button type="submit" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
-      </div>
+		<div class="gf-form-button-row p-y-0">
+			<button type="submit" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
+			<button type="submit" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
+		</div>
 
 
-    </form>
-  </div>
+	</form>
 </div>
 </div>
 
 

+ 0 - 1
public/app/partials/confirm_modal.html

@@ -21,7 +21,6 @@
 			</div>
 			</div>
 		</div>
 		</div>
 
 
-
 		<div class="modal-content-confirm-text" ng-if="confirmText">
 		<div class="modal-content-confirm-text" ng-if="confirmText">
 			<input type="text" class="gf-form-input width-16" style="display: inline-block;" placeholder="Type {{confirmText}} to confirm" ng-model="confirmInput" ng-change="updateConfirmText(confirmInput)">
 			<input type="text" class="gf-form-input width-16" style="display: inline-block;" placeholder="Type {{confirmText}} to confirm" ng-model="confirmInput" ng-change="updateConfirmText(confirmInput)">
 		</div>
 		</div>

+ 5 - 3
public/app/partials/dashboard.html

@@ -2,15 +2,17 @@
 	<dashnav dashboard="ctrl.dashboard"></dashnav>
 	<dashnav dashboard="ctrl.dashboard"></dashnav>
 
 
 	<div class="scroll-canvas scroll-canvas--dashboard" grafana-scrollbar>
 	<div class="scroll-canvas scroll-canvas--dashboard" grafana-scrollbar>
-		<div dash-editor-view class="dash-edit-view"></div>
-		<div class="dashboard-container">
+		<dashboard-settings dashboard="ctrl.dashboard"
+											  ng-if="ctrl.dashboardViewState.state.editview"
+												class="dashboard-settings">
+		</dashboard-settings>
 
 
+		<div class="dashboard-container">
 			<dashboard-submenu ng-if="ctrl.dashboard.meta.submenuEnabled" dashboard="ctrl.dashboard">
 			<dashboard-submenu ng-if="ctrl.dashboard.meta.submenuEnabled" dashboard="ctrl.dashboard">
 			</dashboard-submenu>
 			</dashboard-submenu>
 
 
 			<dashboard-grid get-panel-container="ctrl.getPanelContainer">
 			<dashboard-grid get-panel-container="ctrl.getPanelContainer">
 			</dashboard-grid>
 			</dashboard-grid>
-
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>

+ 0 - 2
public/app/plugins/panel/graph/graph.ts

@@ -1,5 +1,3 @@
-///<reference path="../../../headers/common.d.ts" />
-
 import 'vendor/flot/jquery.flot';
 import 'vendor/flot/jquery.flot';
 import 'vendor/flot/jquery.flot.selection';
 import 'vendor/flot/jquery.flot.selection';
 import 'vendor/flot/jquery.flot.time';
 import 'vendor/flot/jquery.flot.time';

+ 1 - 0
public/sass/_grafana.scss

@@ -86,6 +86,7 @@
 @import "components/dashboard_grid";
 @import "components/dashboard_grid";
 @import "components/dashboard_list";
 @import "components/dashboard_list";
 @import "components/page_header";
 @import "components/page_header";
+@import "components/dashboard_settings";
 @import "components/empty_list_cta";
 @import "components/empty_list_cta";
 
 
 
 

+ 4 - 4
public/sass/_variables.dark.scss

@@ -22,7 +22,6 @@ $gray-5:           #ECECEC;
 $gray-6:           #f4f5f8;
 $gray-6:           #f4f5f8;
 $gray-7:           #fbfbfb;
 $gray-7:           #fbfbfb;
 
 
-// $gray-blue:				 #292a2d;
 $gray-blue:        #212327;
 $gray-blue:        #212327;
 $input-black:      #09090B;
 $input-black:      #09090B;
 
 
@@ -100,6 +99,7 @@ $panel-border:          solid 1px $panel-border-color;
 $panel-drop-zone-bg:    repeating-linear-gradient(-128deg, #111, #111 10px, #191919 10px, #222 20px);
 $panel-drop-zone-bg:    repeating-linear-gradient(-128deg, #111, #111 10px, #191919 10px, #222 20px);
 $panel-header-hover-bg:       $dark-4;
 $panel-header-hover-bg:       $dark-4;
 $panel-header-menu-hover-bg:  $dark-5;
 $panel-header-menu-hover-bg:  $dark-5;
+$panel-edit-shadow: 0 -30px 30px -30px $black;
 
 
 // page header
 // page header
 $page-header-bg: linear-gradient(90deg, #292a2d, black);
 $page-header-bg: linear-gradient(90deg, #292a2d, black);
@@ -192,7 +192,7 @@ $input-label-border-color:       $dark-3;
 $input-invalid-border-color:     lighten($red, 5%);
 $input-invalid-border-color:     lighten($red, 5%);
 
 
 // Search
 // Search
-$search-shadow: 0 0 35px 0 $body-bg;
+$search-shadow: 0 0 30px 0 $black;
 $search-filter-box-bg: $gray-blue;
 $search-filter-box-bg: $gray-blue;
 
 
 // Dropdowns
 // Dropdowns
@@ -229,10 +229,10 @@ $horizontalComponentOffset:       180px;
 // -------------------------
 // -------------------------
 $wellBackground:                  #131517;
 $wellBackground:                  #131517;
 
 
-$navbarHeight:                    52px;
+$navbarHeight:                    55px;
 $navbarBackgroundHighlight:       $dark-3;
 $navbarBackgroundHighlight:       $dark-3;
 $navbarBackground:                $panel-bg;
 $navbarBackground:                $panel-bg;
-$navbarBorder:                    1px solid $body-bg;
+$navbarBorder:                    1px solid $dark-3;
 $navbarShadow:                    0 0 20px black;
 $navbarShadow:                    0 0 20px black;
 
 
 $navbarText:                      $gray-4;
 $navbarText:                      $gray-4;

+ 1 - 0
public/sass/_variables.light.scss

@@ -103,6 +103,7 @@ $panel-border:       solid 1px $panel-border-color;
 $panel-drop-zone-bg: repeating-linear-gradient(-128deg, $body-bg, $body-bg 10px, $gray-6 10px, $gray-6 20px);
 $panel-drop-zone-bg: repeating-linear-gradient(-128deg, $body-bg, $body-bg 10px, $gray-6 10px, $gray-6 20px);
 $panel-header-hover-bg:       $gray-6;
 $panel-header-hover-bg:       $gray-6;
 $panel-header-menu-hover-bg:  $gray-4;
 $panel-header-menu-hover-bg:  $gray-4;
+$panel-edit-shadow: 0 0 30px 20px $black;
 
 
 // Page header
 // Page header
 $page-header-bg: linear-gradient(90deg, $white, $gray-7);
 $page-header-bg: linear-gradient(90deg, $white, $gray-7);

+ 1 - 1
public/sass/_variables.scss

@@ -199,11 +199,11 @@ $form-icon-danger: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www
 // Try to avoid customizing these :)
 // Try to avoid customizing these :)
 $zindex-dropdown:          1000;
 $zindex-dropdown:          1000;
 $zindex-navbar-fixed:      1020;
 $zindex-navbar-fixed:      1020;
+$zindex-sidemenu:          1025;
 $zindex-tooltip:           1030;
 $zindex-tooltip:           1030;
 $zindex-modal-backdrop:    1040;
 $zindex-modal-backdrop:    1040;
 $zindex-modal:             1050;
 $zindex-modal:             1050;
 $zindex-typeahead:         1060;
 $zindex-typeahead:         1060;
-$zindex-sidemenu:          $zindex-navbar-fixed;
 
 
 // Buttons
 // Buttons
 //
 //

+ 1 - 0
public/sass/base/_grafana_icons.scss

@@ -19,6 +19,7 @@
     text-transform: none;
     text-transform: none;
     line-height: 1;
     line-height: 1;
     display: inline-block;
     display: inline-block;
+    vertical-align: middle;
 
 
     /* Better Font Rendering =========== */
     /* Better Font Rendering =========== */
     -webkit-font-smoothing: antialiased;
     -webkit-font-smoothing: antialiased;

+ 80 - 0
public/sass/components/_dashboard_settings.scss

@@ -0,0 +1,80 @@
+.dashboard-settings {
+  opacity: 0;
+  height: 100%;
+  display: flex;
+  flex-direction: row;
+}
+
+.dashboard-page--settings-opening {
+  .dashboard-container {
+    display: none;
+  }
+}
+
+.dashboard-page--settings-open {
+  .dashboard-settings {
+    opacity: 1;
+    transition: opacity 300ms ease-in-out;
+  }
+}
+
+.dashboard-settings__content {
+  flex-grow: 1;
+  min-width: 0;
+  height: 100%;
+  padding: 30px;
+}
+
+.dashboard-settings__aside {
+  padding: 30px 0 0 30px;
+  background: $panel-bg;
+  display: flex;
+  flex-direction: column;
+}
+
+.dashboard-settings__aside-header {
+  color: $text-muted;
+  font-size: $font-size-h3;
+  padding-right: 60px;
+  white-space: nowrap;
+
+  i {
+    font-size: 25px;
+    position: relative;
+    top: 1px;
+    padding-right: 5px;
+  }
+}
+
+.dashboard-settings__header {
+  font-size: $font-size-h3;
+  margin-bottom: 20px;
+}
+
+.dashboard-settings__nav-item {
+  padding: 7px 12px;
+  color: $text-color;
+  font-size: $font-size-md;
+  @include left-brand-border;
+
+  &.active {
+    @include left-brand-border-gradient();
+    background: $page-bg;
+  }
+
+  i {
+    padding-right: 5px;
+  }
+}
+
+.dashboard-settings__aside-actions {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  flex-grow: 1;
+  margin: $spacer*3 $spacer*2 0 0;
+
+  button {
+    margin-bottom: 10px;
+  }
+}

+ 15 - 12
public/sass/components/_empty_list_cta.scss

@@ -1,21 +1,24 @@
-.empty-list-cta {
-    background-color: $search-filter-box-bg;
-    text-align: center;
+.empty-list-cta {
+  background-color: $search-filter-box-bg;
+  text-align: center;
+  padding: $spacer*2;
+  border-radius: $border-radius;
+
+  .grafana-info-box {
+    max-width: 700px;
+    margin: 0 auto;
+  }
 }
 }
 
 
 .empty-list-cta__title {
 .empty-list-cta__title {
-    padding-bottom: 30px;
-    font-style: italic;
+  padding-bottom: $spacer*3;
+  font-style: italic;
 }
 }
 
 
 .empty-list-cta__button {
 .empty-list-cta__button {
-    margin-bottom: 50px;
-}
-
-.empty-list-cta__pro-tip {
-    padding-bottom: 20px;
+  margin-bottom: $spacer*3;
 }
 }
 
 
 .empty-list-cta__pro-tip-link {
 .empty-list-cta__pro-tip-link {
-    margin-left: 5px;
-}
+  margin-left: 5px;
+}

+ 9 - 3
public/sass/components/_gf-form.scss

@@ -66,6 +66,7 @@ $input-border: 1px solid $input-border-color;
   padding: $input-padding-y $input-padding-x;
   padding: $input-padding-y $input-padding-x;
   flex-shrink: 0;
   flex-shrink: 0;
   font-weight: $font-weight-semi-bold;
   font-weight: $font-weight-semi-bold;
+  font-size: $font-size-sm;
 
 
   background-color: $input-label-bg;
   background-color: $input-label-bg;
   display: block;
   display: block;
@@ -84,6 +85,14 @@ $input-border: 1px solid $input-border-color;
   }
   }
 }
 }
 
 
+.gf-form-label + .gf-form-label {
+  margin-right: $gf-form-margin;
+}
+
+.gf-form + .gf-form {
+  margin-right: $gf-form-margin;
+}
+
 .gf-form-pre {
 .gf-form-pre {
   display: block;
   display: block;
   flex-grow: 1;
   flex-grow: 1;
@@ -253,9 +262,6 @@ $input-border: 1px solid $input-border-color;
   margin-right: $gf-form-margin;
   margin-right: $gf-form-margin;
   line-height: $input-line-height;
   line-height: $input-line-height;
   font-size: $font-size-sm;
   font-size: $font-size-sm;
-  box-shadow: none;
-  @include border-radius($label-border-radius-sm);
-  border: $input-btn-border-width solid transparent;
 
 
   flex-shrink: 0;
   flex-shrink: 0;
   flex-grow: 0;
   flex-grow: 0;

+ 0 - 10
public/sass/components/_infobox.scss

@@ -1,13 +1,3 @@
-// .grafana-info-box::before {
-//   content: "\f05a";
-//   font-family:'FontAwesome';
-//   position: absolute;
-//   top: -13px;
-//   left: -8px;
-//   font-size: 20px;
-//   color: $text-color;
-// }
-
 .grafana-info-box {
 .grafana-info-box {
   position: relative;
   position: relative;
   background: $info-box-background;
   background: $info-box-background;

+ 62 - 21
public/sass/components/_navbar.scss

@@ -2,13 +2,57 @@
 .navbar {
 .navbar {
   position: relative;
   position: relative;
   margin-left: 40px;
   margin-left: 40px;
-  // box-shadow: $navbarShadow;
   z-index: $zindex-navbar-fixed;
   z-index: $zindex-navbar-fixed;
-  // background: $navbarBackground;
   height: $navbarHeight;
   height: $navbarHeight;
   padding-right: $spacer;
   padding-right: $spacer;
   display: flex;
   display: flex;
   flex-grow: 1;
   flex-grow: 1;
+  border-bottom: 1px solid transparent;
+  transition: all 350ms ease-in-out;
+}
+
+@mixin navbar-alt-look() {
+  background: $page-header-bg;
+  box-shadow: $search-shadow;
+  border-bottom: $navbarBorder;
+}
+
+
+.dashboard-page--settings-open {
+  .navbar {
+    @include navbar-alt-look();
+  }
+
+  .navbar-button--add-panel,
+  .navbar-button--star,
+  .navbar-button--share,
+  .navbar-button--settings,
+  .navbar-page-btn .fa-caret-down,
+  .gf-timepicker-nav {
+    display: none;
+  }
+
+  .navbar-buttons--close {
+    display: flex;
+  }
+}
+
+.panel-in-fullscreen {
+ .navbar {
+    @include navbar-alt-look();
+  }
+
+  .navbar-button--add-panel,
+  .navbar-button--star,
+  .navbar-button--save,
+  .navbar-button--settings,
+  .navbar-page-btn .fa-caret-down {
+    display: none;
+  }
+
+  .navbar-buttons--close {
+    display: flex;
+  }
 }
 }
 
 
 .navbar-page-btn {
 .navbar-page-btn {
@@ -19,8 +63,9 @@
   margin: 0;
   margin: 0;
   color: darken($link-color, 5%);
   color: darken($link-color, 5%);
   font-size: $font-size-lg;
   font-size: $font-size-lg;
-  padding: 1rem 1rem 0.75rem 1rem;
+  padding-left: 1rem;
   min-height: $navbarHeight;
   min-height: $navbarHeight;
+  line-height: $navbarHeight;
 
 
   .fa-caret-down {
   .fa-caret-down {
     font-size: 60%;
     font-size: 60%;
@@ -43,26 +88,17 @@
   }
   }
 }
 }
 
 
-.navbar-page-btn-wrapper {
-  display: inline-block;
-  position: relative;
-}
-
-.navbar-section-wrapper {
-  position: relative;
-  float: left;
-}
-
-.navbar-mini-btn-wrapper {
-  padding: 15px;
-}
-
 .navbar-buttons {
 .navbar-buttons {
   height: $navbarHeight;
   height: $navbarHeight;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   justify-content: flex-end;
   justify-content: flex-end;
   margin-right: $spacer;
   margin-right: $spacer;
+
+  &--close {
+    display: none;
+    margin-right: 0;
+  }
 }
 }
 
 
 .navbar__spacer {
 .navbar__spacer {
@@ -74,7 +110,7 @@
 
 
   display: inline-block;
   display: inline-block;
   font-weight: $btn-font-weight;
   font-weight: $btn-font-weight;
-  padding: 8px 11px;
+  padding: 6px 11px;
   line-height: 16px;
   line-height: 16px;
   color: $text-muted;
   color: $text-muted;
   border: 1px solid $navbar-button-border;
   border: 1px solid $navbar-button-border;
@@ -90,7 +126,7 @@
   }
   }
 
 
   &--add-panel {
   &--add-panel {
-    padding: 5px 10px;
+    padding: 3px 10px;
 
 
     .gicon {
     .gicon {
       font-size: 22px;
       font-size: 22px;
@@ -98,7 +134,7 @@
   }
   }
 
 
   &--tight {
   &--tight {
-    padding: 9px 4px;
+    padding: 7px 4px;
 
 
     .fa {
     .fa {
       font-size: 14px;
       font-size: 14px;
@@ -106,6 +142,10 @@
       top: 2px;
       top: 2px;
     }
     }
   }
   }
+
+  &--primary {
+    @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl);
+  }
 }
 }
 
 
 @include media-breakpoint-up(sm) {
 @include media-breakpoint-up(sm) {
@@ -115,7 +155,8 @@
 
 
   .sidemenu-open {
   .sidemenu-open {
     .navbar {
     .navbar {
-      margin-left: 15px;
+      padding-left: 15px;
+      margin-left: 0;
     }
     }
   }
   }
 
 

+ 4 - 0
public/sass/components/_query_editor.scss

@@ -33,6 +33,10 @@
   .gf-form-label {
   .gf-form-label {
     margin-right: 2px;
     margin-right: 2px;
   }
   }
+
+  .gf-form + .gf-form {
+    margin-right: 0;
+  }
 }
 }
 
 
 .gf-form-query-content {
 .gf-form-query-content {

+ 1 - 1
public/sass/components/_switch.scss

@@ -27,6 +27,7 @@
     background: $input-bg;
     background: $input-bg;
     border: 1px solid $input-border-color;
     border: 1px solid $input-border-color;
     border-left: none;
     border-left: none;
+    border-radius: $input-border-radius;
   }
   }
 
 
   input + label::before, input + label::after {
   input + label::before, input + label::after {
@@ -45,7 +46,6 @@
     display: flex;
     display: flex;
     flex-direction: column;
     flex-direction: column;
     justify-content: center;
     justify-content: center;
-    border-radius: $input-border-radius;
   }
   }
 
 
   &:hover {
   &:hover {

+ 2 - 1
public/sass/components/_tabbed_view.scss

@@ -6,7 +6,8 @@
     padding: 0;
     padding: 0;
 
 
     .tabbed-view-header {
     .tabbed-view-header {
-      padding: 1.5em 1rem 0 1rem;
+      padding: 0px 25px;
+      background: none;
     }
     }
   }
   }
 }
 }

+ 6 - 0
public/sass/components/_tabs.scss

@@ -18,11 +18,17 @@
   border: solid transparent;
   border: solid transparent;
   border-width: 0 1px 1px;
   border-width: 0 1px 1px;
   border-radius: 3px 3px 0 0;
   border-radius: 3px 3px 0 0;
+  color: $text-color;
 
 
   i {
   i {
     margin-right: 5px;
     margin-right: 5px;
   }
   }
 
 
+  .gicon {
+    position: relative;
+    top: -2px;
+  }
+
   &:hover,
   &:hover,
   &:focus {
   &:focus {
     color: $link-hover-color;
     color: $link-hover-color;

+ 8 - 7
public/sass/components/_timepicker.scss

@@ -13,17 +13,18 @@
 }
 }
 
 
 .gf-timepicker-dropdown {
 .gf-timepicker-dropdown {
-  margin: -8px -10px 10px 5px;
+  position: absolute;
+  top: $navbarHeight;
+  right: 0;
   padding: 10px 20px;
   padding: 10px 20px;
-  float: right;
-  background-color: $panel-bg;
-  @include box-shadow($navbarDropdownShadow);
+  background-color: $page-bg;
+  border-radius: 0 0 0 4px;
+  box-shadow: $search-shadow;
 }
 }
 
 
 .gf-timepicker-absolute-section {
 .gf-timepicker-absolute-section {
   width: 290px;
   width: 290px;
   float: left;
   float: left;
-  border-right: 1px solid $divider-border-color;
   padding: 0 10px;
   padding: 0 10px;
   select {
   select {
     width: 183px;
     width: 183px;
@@ -41,7 +42,7 @@
   font-size: 75%;
   font-size: 75%;
   padding: 3px;
   padding: 3px;
   border-radius: 2px;
   border-radius: 2px;
-  font-weight: bold;
+  font-weight: 500;
   margin-left: 4px;
   margin-left: 4px;
 }
 }
 
 
@@ -59,7 +60,7 @@
     li.active {
     li.active {
       border-bottom: 1px solid $blue;
       border-bottom: 1px solid $blue;
       margin: 3px 0;
       margin: 3px 0;
-      font-weight: bold;
+      font-weight: 500;
     }
     }
   }
   }
 }
 }

+ 1 - 15
public/sass/components/_view_states.scss

@@ -1,19 +1,5 @@
-@mixin hide-controls() {
-  .add-row-panel-hint,
-  .dash-row-menu-container {
-    display: none;
-  }
-  .panel-drop-zone {
-    visibility: hidden;
-  }
-}
-
-.hide-controls {
-  @include hide-controls();
-}
 
 
 .page-kiosk-mode {
 .page-kiosk-mode {
-  @include hide-controls();
   dashnav {
   dashnav {
     display: none;
     display: none;
   }
   }
@@ -45,7 +31,7 @@
     background: transparent;
     background: transparent;
     transform: translate3d(-40px, 0, 0);
     transform: translate3d(-40px, 0, 0);
     transition: all 1.5s ease-in-out 1s;
     transition: all 1.5s ease-in-out 1s;
-    .icon-gf {
+    i {
       opacity: 0;
       opacity: 0;
       transition: all 1.5s ease-in-out 1s;
       transition: all 1.5s ease-in-out 1s;
     }
     }

+ 2 - 16
public/sass/pages/_dashboard.scss

@@ -217,20 +217,6 @@ div.flot-text {
   }
   }
 }
 }
 
 
-.dash-edit-view {
-  opacity: 0;
-
-  &--open{
-    opacity: 1;
-    transition: opacity 200ms ease-in-out;
-  }
-}
-
-.dashboard-nav-shadow {
-  box-shadow: 0 0 20px black;
-  height: 10px;
-  position: absolute;
-  width: 100%;
-  z-index: 1;
-  top: -10px;
+.panel-full-edit {
+  margin: $dashboard-padding (-$dashboard-padding) 0 (-$dashboard-padding);
 }
 }