Просмотр исходного кода

Merge branch 'master' into alerting_definitions

bergquist 9 лет назад
Родитель
Сommit
5bc3672296
40 измененных файлов с 1104 добавлено и 825 удалено
  1. 9 0
      CHANGELOG.md
  2. 0 5
      public/app/core/components/info_popover.ts
  3. 2 2
      public/app/core/directives/dropdown_typeahead.js
  4. 7 4
      public/app/core/directives/metric_segment.js
  5. 0 2
      public/app/core/services/context_srv.ts
  6. 1 0
      public/app/features/panel/all.js
  7. 106 0
      public/app/features/panel/metrics_ds_selector.ts
  8. 0 12
      public/app/features/panel/metrics_panel_ctrl.ts
  9. 60 1
      public/app/features/panel/partials/query_editor_row.html
  10. 0 34
      public/app/features/panel/query_ctrl.ts
  11. 100 2
      public/app/features/panel/query_editor_row.ts
  12. 14 51
      public/app/partials/metrics.html
  13. 2 2
      public/app/plugins/datasource/cloudwatch/partials/query.editor.html
  14. 51 50
      public/app/plugins/datasource/cloudwatch/partials/query.parameter.html
  15. 63 105
      public/app/plugins/datasource/elasticsearch/partials/bucket_agg.html
  16. 61 117
      public/app/plugins/datasource/elasticsearch/partials/metric_agg.html
  17. 28 29
      public/app/plugins/datasource/elasticsearch/partials/query.editor.html
  18. 43 0
      public/app/plugins/datasource/elasticsearch/query_ctrl.ts
  19. 2 2
      public/app/plugins/datasource/elasticsearch/query_def.js
  20. 6 4
      public/app/plugins/datasource/grafana/partials/query.editor.html
  21. 2 2
      public/app/plugins/datasource/graphite/add_graphite_func.js
  22. 21 15
      public/app/plugins/datasource/graphite/partials/query.editor.html
  23. 1 5
      public/app/plugins/datasource/influxdb/influx_query.ts
  24. 86 63
      public/app/plugins/datasource/influxdb/partials/query.editor.html
  25. 1 1
      public/app/plugins/datasource/influxdb/partials/query.options.html
  26. 1 1
      public/app/plugins/datasource/influxdb/partials/query_part.html
  27. 10 1
      public/app/plugins/datasource/influxdb/query_ctrl.ts
  28. 250 220
      public/app/plugins/datasource/opentsdb/partials/query.editor.html
  29. 48 74
      public/app/plugins/datasource/prometheus/partials/query.editor.html
  30. 1 0
      public/sass/_grafana.scss
  31. 3 3
      public/sass/_variables.dark.scss
  32. 1 1
      public/sass/_variables.light.scss
  33. 6 2
      public/sass/base/_type.scss
  34. 1 1
      public/sass/components/_drop.scss
  35. 40 8
      public/sass/components/_gf-form.scss
  36. 57 4
      public/sass/components/_query_editor.scss
  37. 11 0
      public/sass/components/_query_part.scss
  38. 1 1
      public/sass/mixins/_drop_element.scss
  39. 2 1
      public/sass/utils/_utils.scss
  40. 6 0
      public/sass/utils/_widths.scss

+ 9 - 0
CHANGELOG.md

@@ -14,6 +14,15 @@
 * **Singlestat**: Fixes prefix an postfix for gauges, fixes [#4812](https://github.com/grafana/grafana/issues/4812)
 * **Singlestat**: Fixes auto-refresh on change for some options, fixes [#4809](https://github.com/grafana/grafana/issues/4809)
 
+### Breaking changes
+**Data Source Query Editors**: Issue [#3900](https://github.com/grafana/grafana/issues/3900)
+
+Query editors have been updated to use the new form styles. External data source plugins needs to be
+updated to work. Sorry to introduce breaking change this late in beta phase. We wanted to get this change
+in before 3.0 stable is released so we don't have to break data sources in next release (3.1). If you are
+a data source plugin author and want help for how the new form styles work please ask for help in
+slack channel (link to slack channel in readme).
+
 # 3.0.0-beta5 (2016-04-15)
 
 ### Bug fixes

+ 0 - 5
public/app/core/components/info_popover.ts

@@ -11,11 +11,6 @@ export function infoPopover() {
     template: '<i class="fa fa-info-circle"></i>',
     transclude: true,
     link: function(scope, elem, attrs, ctrl, transclude) {
-      // var inputElem = elem.prev();
-      // if (inputElem.length === 0) {
-      //   console.log('Failed to find input element for popover');
-      //   return;
-      // }
 
       var offset = attrs.offset || '0 -10px';
       var position = attrs.position || 'right middle';

+ 2 - 2
public/app/core/directives/dropdown_typeahead.js

@@ -9,10 +9,10 @@ function (_, $, coreModule) {
   coreModule.default.directive('dropdownTypeahead', function($compile) {
 
     var inputTemplate = '<input type="text"'+
-      ' class="tight-form-input input-medium tight-form-input"' +
+      ' class="gf-form-input input-medium tight-form-input"' +
       ' spellcheck="false" style="display:none"></input>';
 
-    var buttonTemplate = '<a  class="tight-form-item tight-form-func dropdown-toggle"' +
+    var buttonTemplate = '<a  class="gf-form-label tight-form-func dropdown-toggle"' +
       ' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
       ' data-placement="top"><i class="fa fa-plus"></i></a>';
 

+ 7 - 4
public/app/core/directives/metric_segment.js

@@ -8,10 +8,13 @@ function (_, $, coreModule) {
 
   coreModule.default.directive('metricSegment', function($compile, $sce) {
     var inputTemplate = '<input type="text" data-provide="typeahead" ' +
-      ' class="tight-form-clear-input input-medium"' +
+      ' class="gf-form-input input-medium"' +
       ' spellcheck="false" style="display:none"></input>';
 
-    var buttonTemplate = '<a class="tight-form-item" ng-class="segment.cssClass" ' +
+    var linkTemplate = '<a class="gf-form-label" ng-class="segment.cssClass" ' +
+      'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>';
+
+    var selectTemplate = '<a class="gf-form-input gf-form-input--dropdown" ng-class="segment.cssClass" ' +
       'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>';
 
     return {
@@ -20,9 +23,9 @@ function (_, $, coreModule) {
         getOptions: "&",
         onChange: "&",
       },
-      link: function($scope, elem) {
+      link: function($scope, elem, attrs) {
         var $input = $(inputTemplate);
-        var $button = $(buttonTemplate);
+        var $button = $(attrs.styleMode === 'select' ? selectTemplate : linkTemplate);
         var segment = $scope.segment;
         var options = null;
         var cancelBlur = null;

+ 0 - 2
public/app/core/services/context_srv.ts

@@ -25,7 +25,6 @@ export class ContextSrv {
   isGrafanaAdmin: any;
   isEditor: any;
   sidemenu: any;
-  lightTheme: any;
 
   constructor() {
     this.pinned = store.getBool('grafana.sidemenu.pinned', false);
@@ -41,7 +40,6 @@ export class ContextSrv {
     }
 
     this.version = config.buildInfo.version;
-    this.lightTheme = false;
     this.user = new User();
     this.isSignedIn = this.user.isSignedIn;
     this.isGrafanaAdmin = this.user.isGrafanaAdmin;

+ 1 - 0
public/app/features/panel/all.js

@@ -5,4 +5,5 @@ define([
   './query_ctrl',
   './panel_editor_tab',
   './query_editor_row',
+  './metrics_ds_selector',
 ], function () {});

+ 106 - 0
public/app/features/panel/metrics_ds_selector.ts

@@ -0,0 +1,106 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+
+var module = angular.module('grafana.directives');
+
+var template = `
+<div class="gf-form-group">
+  <div class="gf-form-inline">
+    <div class="gf-form">
+      <label class="gf-form-label">
+        <i class="icon-gf icon-gf-datasource"></i>
+      </label>
+      <label class="gf-form-label">
+        Panel data source
+      </label>
+
+      <metric-segment segment="ctrl.dsSegment" style-mode="select"
+                      get-options="ctrl.getOptions()"
+                      on-change="ctrl.datasourceChanged()"></metric-segment>
+    </div>
+
+    <div class="gf-form gf-form--offset-1">
+      <button class="btn btn-inverse gf-form-btn" ng-click="ctrl.addDataQuery()" ng-hide="ctrl.current.meta.mixed">
+        <i class="fa fa-plus"></i>&nbsp;
+        Add query
+      </button>
+
+      <div class="dropdown" ng-if="ctrl.current.meta.mixed">
+        <button class="btn btn-inverse dropdown-toggle gf-form-btn" data-toggle="dropdown">
+          Add Query&nbsp;<span class="fa fa-caret-down"></span>
+        </button>
+
+        <ul class="dropdown-menu" role="menu">
+          <li ng-repeat="datasource in ctrl.datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
+            <a ng-click="ctrl.addDataQuery(datasource);">{{datasource.name}}</a>
+          </li>
+        </ul>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+
+export class MetricsDsSelectorCtrl {
+  dsSegment: any;
+  dsName: string;
+  panelCtrl: any;
+  datasources: any[];
+  current: any;
+
+  /** @ngInject */
+  constructor(private uiSegmentSrv, datasourceSrv) {
+    this.datasources = datasourceSrv.getMetricSources();
+
+    var dsValue = this.panelCtrl.panel.datasource || null;
+
+    for (let ds of this.datasources) {
+      if (ds.value === dsValue) {
+        this.current = ds;
+      }
+    }
+
+    this.dsSegment = uiSegmentSrv.newSegment(this.current.name);
+  }
+
+  getOptions() {
+    return Promise.resolve(this.datasources.map(value => {
+      return this.uiSegmentSrv.newSegment(value.name);
+    }));
+  }
+
+  datasourceChanged() {
+    var ds = _.findWhere(this.datasources, {name: this.dsSegment.value});
+    if (ds) {
+      this.current = ds;
+      this.panelCtrl.setDatasource(ds);
+    }
+  }
+
+  addDataQuery(datasource) {
+    var target: any = {isNew: true};
+
+    if (datasource) {
+      target.datasource = datasource.name;
+    }
+
+    this.panelCtrl.panel.targets.push(target);
+  }
+}
+
+module.directive('metricsDsSelector', function() {
+  return {
+    restrict: 'E',
+    template: template,
+    controller: MetricsDsSelectorCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+    transclude: true,
+    scope: {
+      panelCtrl: "="
+    }
+  };
+});

+ 0 - 12
public/app/features/panel/metrics_panel_ctrl.ts

@@ -27,7 +27,6 @@ class MetricsPanelCtrl extends PanelCtrl {
   resolution: any;
   timeInfo: any;
   skipDataOnInit: boolean;
-  datasources: any[];
   dataStream: any;
   dataSubscription: any;
 
@@ -52,7 +51,6 @@ class MetricsPanelCtrl extends PanelCtrl {
   private onInitMetricsPanelEditMode() {
     this.addEditorTab('Metrics', 'public/app/partials/metrics.html');
     this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html');
-    this.datasources = this.datasourceSrv.getMetricSources();
   }
 
   private onMetricsPanelRefresh() {
@@ -249,16 +247,6 @@ class MetricsPanelCtrl extends PanelCtrl {
     this.datasource = null;
     this.refresh();
   }
-
-  addDataQuery(datasource) {
-    var target: any = {};
-
-    if (datasource) {
-      target.datasource = datasource.name;
-    }
-
-    this.panel.targets.push(target);
-  }
 }
 
 export {MetricsPanelCtrl};

+ 60 - 1
public/app/features/panel/partials/query_editor_row.html

@@ -1,4 +1,63 @@
-<div class="tight-form">
+
+<div class="gf-form-query">
+	<div class="gf-form">
+    <label class="gf-form-label gf-form-query-letter-cell">
+      <a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()">
+        <span  ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret">
+          <i class="fa fa-caret-down" ng-hide="ctrl.collapsed"></i>
+          <i class="fa fa-caret-right" ng-show="ctrl.collapsed"></i>
+        </span>
+        <span class="gf-form-query-letter-cell-letter">{{ctrl.target.refId}}</span>
+        <em class="gf-form-query-letter-cell-ds" ng-show="ctrl.target.datasource">({{ctrl.target.datasource}})</em>
+      </a>
+		</label>
+  </div>
+
+	<div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
+		<div class="gf-form">
+			<label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
+				{{ctrl.collapsedText}}
+			</label>
+		</div>
+	</div>
+
+	<div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed">
+	</div>
+
+	<div class="gf-form">
+		<label class="gf-form-label dropdown">
+			<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1">
+				<i class="fa fa-bars"></i>
+			</a>
+			<ul class="dropdown-menu pull-right" role="menu">
+				<li role="menuitem" ng-if="ctrl.hasTextEditMode">
+					<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
+				</li>
+				<li role="menuitem">
+					<a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a>
+				</li>
+				<li role="menuitem">
+					<a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a>
+				</li>
+				<li role="menuitem">
+					<a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</a>
+				</li>
+			</ul>
+		</label>
+    <label class="gf-form-label">
+			<a ng-click="ctrl.toggleHideQuery()" role="menuitem">
+				<i class="fa fa-eye"></i>
+			</a>
+		</label>
+		<label class="gf-form-label">
+			<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)">
+				<i class="fa fa-trash"></i>
+			</a>
+		</label>
+	</div>
+</div>
+
+<div class="tight-form" ng-if="false">
 	<ul class="tight-form-list pull-right">
 		<li ng-show="ctrl.error" class="tight-form-item">
 			<a bs-tooltip="ctrl.error" style="color: rgb(229, 189, 28)" role="menuitem">

+ 0 - 34
public/app/features/panel/query_ctrl.ts

@@ -13,45 +13,11 @@ export class QueryCtrl {
 
   constructor(public $scope, private $injector) {
     this.panel = this.panelCtrl.panel;
-
-    if (!this.target.refId) {
-      this.target.refId = this.getNextQueryLetter();
-    }
-  }
-
-  getNextQueryLetter() {
-    var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-
-    return _.find(letters, refId => {
-      return _.every(this.panel.targets, function(other) {
-        return other.refId !== refId;
-      });
-    });
-  }
-
-  removeQuery() {
-    this.panel.targets = _.without(this.panel.targets, this.target);
-    this.panelCtrl.refresh();
-  };
-
-  duplicateQuery() {
-    var clone = angular.copy(this.target);
-    clone.refId = this.getNextQueryLetter();
-    this.panel.targets.push(clone);
-  }
-
-  moveQuery(direction) {
-    var index = _.indexOf(this.panel.targets, this.target);
-    _.move(this.panel.targets, index, index + direction);
   }
 
   refresh() {
     this.panelCtrl.refresh();
   }
 
-  toggleHideQuery() {
-    this.target.hide = !this.target.hide;
-    this.panelCtrl.refresh();
-  }
 }
 

+ 100 - 2
public/app/features/panel/query_editor_row.ts

@@ -1,17 +1,115 @@
 ///<reference path="../../headers/common.d.ts" />
 
 import angular from 'angular';
-import $ from 'jquery';
+import _ from 'lodash';
 
 var module = angular.module('grafana.directives');
 
+export class QueryRowCtrl {
+  collapsedText: string;
+  canCollapse: boolean;
+  getCollapsedText: any;
+  target: any;
+  queryCtrl: any;
+  panelCtrl: any;
+  panel: any;
+  collapsed: any;
+
+  constructor() {
+    this.panelCtrl = this.queryCtrl.panelCtrl;
+    this.target = this.queryCtrl.target;
+    this.panel = this.panelCtrl.panel;
+
+    if (!this.target.refId) {
+      this.target.refId = this.getNextQueryLetter();
+    }
+
+    this.toggleCollapse(true);
+    if (this.target.isNew) {
+      delete this.target.isNew;
+      this.toggleCollapse(false);
+    }
+  }
+
+  toggleHideQuery() {
+    this.target.hide = !this.target.hide;
+    this.panelCtrl.refresh();
+  }
+
+  getNextQueryLetter() {
+    var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+    return _.find(letters, refId => {
+      return _.every(this.panel.targets, function(other) {
+        return other.refId !== refId;
+      });
+    });
+  }
+
+  toggleCollapse(init) {
+    if (!this.canCollapse) {
+      return;
+    }
+
+    if (!this.panelCtrl.__collapsedQueryCache) {
+      this.panelCtrl.__collapsedQueryCache = {};
+    }
+
+    if (init) {
+      this.collapsed = this.panelCtrl.__collapsedQueryCache[this.target.refId] !== false;
+    } else {
+      this.collapsed = !this.collapsed;
+      this.panelCtrl.__collapsedQueryCache[this.target.refId] = this.collapsed;
+    }
+
+    try {
+      this.collapsedText = this.queryCtrl.getCollapsedText();
+    } catch (e) {
+      var err = e.message || e.toString();
+      this.collapsedText = 'Error: ' + err;
+    }
+  }
+
+  toggleEditorMode() {
+    if (this.canCollapse && this.collapsed) {
+      this.collapsed = false;
+    }
+
+    this.queryCtrl.toggleEditorMode();
+  }
+
+  removeQuery() {
+    delete this.panelCtrl.__collapsedQueryCache[this.target.refId];
+    this.panel.targets = _.without(this.panel.targets, this.target);
+    this.panelCtrl.refresh();
+  }
+
+  duplicateQuery() {
+    var clone = angular.copy(this.target);
+    clone.refId = this.getNextQueryLetter();
+    this.panel.targets.push(clone);
+  }
+
+  moveQuery(direction) {
+    var index = _.indexOf(this.panel.targets, this.target);
+    _.move(this.panel.targets, index, index + direction);
+  }
+}
+
 /** @ngInject **/
 function queryEditorRowDirective() {
   return {
     restrict: 'E',
+    controller: QueryRowCtrl,
+    bindToController: true,
+    controllerAs: "ctrl",
     templateUrl: 'public/app/features/panel/partials/query_editor_row.html',
     transclude: true,
-    scope: {ctrl: "="},
+    scope: {
+      queryCtrl: "=",
+      canCollapse: "=",
+      hasTextEditMode: "=",
+    },
   };
 }
 

+ 14 - 51
public/app/partials/metrics.html

@@ -1,56 +1,19 @@
-<div class="editor-row">
-
-	<div class="tight-form-container">
-		<div ng-repeat="target in ctrl.panel.targets" ng-class="{'tight-form-disabled': target.hide}">
-			<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
-				<plugin-component type="query-ctrl">
-				</plugin-component>
-			</rebuild-on-change>
-		</div>
-	</div>
-
-	<div style="margin: 20px 0 0 0">
-		<button class="btn btn-inverse" ng-click="ctrl.addDataQuery()" ng-hide="ctrl.datasource.meta.mixed">
-			<i class="fa fa-plus"></i>&nbsp;
-			Query
-		</button>
-
-		<div class="dropdown" ng-if="ctrl.datasource.meta.mixed">
-			<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown">
-				<i class="fa fa-plus"></i>&nbsp;
-				Query &nbsp; <span class="caret"></span>
-			</button>
-
-			<ul class="dropdown-menu" role="menu">
-				<li ng-repeat="datasource in ctrl.datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
-					<a ng-click="ctrl.addDataQuery(datasource);">{{datasource.name}}</a>
-				</li>
-			</ul>
-		</div>
-
-	</div>
-
-	<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
-		<plugin-component type="query-options-ctrl">
-		</plugin-component>
-	</rebuild-on-change>
 
+<div class="query-editor-rows gf-form-group">
+  <div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
+    <rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
+      <plugin-component type="query-ctrl">
+      </plugin-component>
+    </rebuild-on-change>
+  </div>
 </div>
 
-<div class="editor-row">
-
-	<div class="pull-right dropdown" style="margin-right: 10px;">
-		<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown" bs-tooltip="'Datasource'">
-			<i class="fa fa-database"></i>&nbsp;
-			{{ctrl.datasource.name}} &nbsp; <span class="caret"></span>
-		</button>
+<metrics-ds-selector panel-ctrl="ctrl"></metrics-ds-selector>
 
-		<ul class="dropdown-menu" role="menu">
-			<li ng-repeat="datasource in ctrl.datasources" role="menuitem">
-				<a ng-click="ctrl.setDatasource(datasource);">{{datasource.name}}</a>
-			</li>
-		</ul>
-	</div>
-
-	<div class="clearfix"></div>
+<div class="gf-form-group">
+  <rebuild-on-change property="ctrl.panel.datasource" show-null="true">
+    <plugin-component type="query-options-ctrl">
+    </plugin-component>
+  </rebuild-on-change>
 </div>
+

+ 2 - 2
public/app/plugins/datasource/cloudwatch/partials/query.editor.html

@@ -1,4 +1,4 @@
-<query-editor-row ctrl="ctrl">
+<query-editor-row query-ctrl="ctrl" can-collapse="false">
+	<cloudwatch-query-parameter target="ctrl.target" datasource="ctrl.datasource" on-change="ctrl.refresh()"></cloudwatch-query-parameter>
 </query-editor-row>
 
-<cloudwatch-query-parameter target="ctrl.target" datasource="ctrl.datasource" on-change="ctrl.refresh()"></cloudwatch-query-parameter>

+ 51 - 50
public/app/plugins/datasource/cloudwatch/partials/query.parameter.html

@@ -1,58 +1,59 @@
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
-			Metric
-		</li>
-		<li>
-			<metric-segment segment="regionSegment" get-options="getRegions()" on-change="regionChanged()"></metric-segment>
-		</li>
-		<li>
-			<metric-segment segment="namespaceSegment" get-options="getNamespaces()" on-change="namespaceChanged()"></metric-segment>
-		</li>
-		<li>
-			<metric-segment segment="metricSegment" get-options="getMetrics()" on-change="metricChanged()"></metric-segment>
-		</li>
-		<li class="tight-form-item query-keyword">
-			Stats
-		</li>
-		<li ng-repeat="segment in statSegments">
-			<metric-segment segment="segment" get-options="getStatSegments(segment, $index)" on-change="statSegmentChanged(segment, $index)"></metric-segment>
-		</li>
-	</ul>
+<div class="gf-form-inline">
+	<div class="gf-form">
+		<label class="gf-form-label query-keyword width-7">Metric</label>
 
-	<div class="clearfix"></div>
+		<metric-segment segment="regionSegment" get-options="getRegions()" on-change="regionChanged()"></metric-segment>
+		<metric-segment segment="namespaceSegment" get-options="getNamespaces()" on-change="namespaceChanged()"></metric-segment>
+		<metric-segment segment="metricSegment" get-options="getMetrics()" on-change="metricChanged()"></metric-segment>
+	</div>
+
+	<div class="gf-form">
+		<label class="gf-form-label query-keyword">Stats</label>
+	</div>
+
+	<div class="gf-form" ng-repeat="segment in statSegments">
+		<metric-segment segment="segment" get-options="getStatSegments(segment, $index)" on-change="statSegmentChanged(segment, $index)"></metric-segment>
+	</div>
+
+	<div class="gf-form gf-form--grow">
+		<div class="gf-form-label gf-form-label--grow"></div>
+	</div>
 </div>
 
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
-			Dimensions
-		</li>
-		<li ng-repeat="segment in dimSegments">
-			<metric-segment segment="segment" get-options="getDimSegments(segment, $index)" on-change="dimSegmentChanged(segment, $index)"></metric-segment>
-		</li>
-	</ul>
+<div class="gf-form-inline">
+	<div class="gf-form">
+		<label class="gf-form-label query-keyword width-7">Dimensions</label>
+		<metric-segment ng-repeat="segment in dimSegments" segment="segment" get-options="getDimSegments(segment, $index)" on-change="dimSegmentChanged(segment, $index)"></metric-segment>
+	</div>
 
-	<div class="clearfix"></div>
+	<div class="gf-form gf-form--grow">
+		<div class="gf-form-label gf-form-label--grow"></div>
+	</div>
 </div>
 
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
+<div class="gf-form-inline">
+	<div class="gf-form">
+		<label class="gf-form-label query-keyword width-7">
 			Period
-			<tip>Interval between points in seconds</tip>
-		</li>
-		<li>
-			<input type="text" class="input-mini tight-form-input" ng-model="target.period" spellcheck='false' placeholder="auto" ng-model-onblur ng-change="onChange()" />
-		</li>
-		<li class="tight-form-item query-keyword">
-			Alias
-			<tip>{{metric}} {{stat}} {{namespace}} {{region}} {{DIMENSION_NAME}}</tip>
-		</li>
-		<li>
-			<input type="text" class="input-xlarge tight-form-input"  ng-model="target.alias" spellcheck='false' ng-model-onblur ng-change="onChange()">
-		</li>
-	</ul>
-	<div class="clearfix"></div>
+			<info-popover mode="right-normal">Interval between points in seconds</info-popover>
+		</label>
+		<input type="text" class="gf-form-input" ng-model="target.period" spellcheck='false' placeholder="auto" ng-model-onblur ng-change="onChange()" />
+	</div>
+	<div class="gf-form max-width-30">
+		<label class="gf-form-label query-keyword width-7">Alias</label>
+		<input type="text" class="gf-form-input"  ng-model="target.alias" spellcheck='false' ng-model-onblur ng-change="onChange()">
+		<info-popover mode="right-absolute">
+			Alias replacement variables:
+			<ul ng-non-bindable>
+				<li>{{metric}}</li>
+				<li>{{stat}}</li>
+				<li>{{namespace}}</li>
+				<li>{{region}}</li>
+				<li>{{DIMENSION_NAME}}</li>
+			</ul>
+		</info-popover>
+	</div>
+	<div class="gf-form gf-form--grow">
+		<div class="gf-form-label gf-form-label--grow"></div>
+	</div>
 </div>
-

+ 63 - 105
public/app/plugins/datasource/elasticsearch/partials/bucket_agg.html

@@ -1,131 +1,89 @@
-<div class="tight-form">
-	<ul class="tight-form-list">
-		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
+<div class="gf-form-inline">
+	<div class="gf-form">
+		<label class="gf-form-label query-keyword width-7">
 			<span ng-show="isFirst">Group by</span>
 			<span ng-hide="isFirst">Then by</span>
-		</li>
-		<li>
-			<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
-		</li>
-		<li ng-if="agg.field">
-			<metric-segment-model property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment>
-		</li>
-		<li ng-if="!agg.field">
-			<span class="tight-form-item tight-form-item-xxlarge">&nbsp;</span>
-		</li>
-		<li class="tight-form-item last" ng-if="settingsLinkText">
+		</label>
+
+		<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="width-10"></metric-segment-model>
+		<metric-segment-model ng-if="agg.field" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment>
+	</div>
+
+	<div class="gf-form gf-form--grow">
+		<label class="gf-form-label gf-form-label--grow">
 			<a ng-click="toggleOptions()">
 				<i class="fa fa-caret-down" ng-show="showOptions"></i>
 				<i class="fa fa-caret-right" ng-hide="showOptions"></i>
 				{{settingsLinkText}}
 			</a>
-		</li>
-	</ul>
+		</label>
+	</div>
 
-	<ul class="tight-form-list pull-right">
-		<li class="tight-form-item last" ng-if="isFirst">
+	<div class="gf-form">
+		<label class="gf-form-label" ng-if="isFirst">
 			<a class="pointer" ng-click="addBucketAgg()"><i class="fa fa-plus"></i></a>
-		</li>
-		<li class="tight-form-item last">
+		</label>
+		<label class="gf-form-label">
 			<a class="pointer" ng-click="removeBucketAgg()"><i class="fa fa-minus"></i></a>
-		</li>
-	</ul>
-	<div class="clearfix"></div>
+		</label>
+	</div>
 </div>
 
-<div class="tight-form" ng-if="showOptions">
-	<div class="tight-form-inner-box" ng-if="agg.type === 'date_histogram'">
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 170px">
-					Interval
-				</li>
-				<li>
-					<metric-segment-model property="agg.settings.interval" get-options="getIntervalOptions()" on-change="onChangeInternal()" css-class="last" custom="true"></metric-segment-model>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+<div class="gf-form-group" ng-if="showOptions">
+	<div ng-if="agg.type === 'date_histogram'">
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label width-10">Interval</label>
+			<metric-segment-model property="agg.settings.interval" get-options="getIntervalOptions()" on-change="onChangeInternal()" css-class="width-12" custom="true"></metric-segment-model>
 		</div>
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 170px">
-					Min Doc Count
-				</li>
-				<li>
-					<input type="number" class="tight-form-input" ng-model="agg.settings.min_doc_count" ng-blur="onChangeInternal()">
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label width-10">Min Doc Count</label>
+			<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.min_doc_count" ng-blur="onChangeInternal()">
 		</div>
-		<div class="tight-form last">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 170px">
-					Trim edges points
-				</li>
-				<li>
-					<input class="tight-form-input" type="number" ng-model="agg.settings.trimEdges" ng-change="onChangeInternal()">
-				</li>
-				<li class="tight-form-item last">
-					<i class="fa fa-question-circle" bs-tooltip="'Trim the edges on the timeseries x datapoints'" data-placement="right"></i>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label width-10">
+				Trim edges
+				<info-popover mode="right-normal">
+					Trim the edges on the timeseries datapoints
+				</info-popover>
+			</label>
+			<input class="gf-form-input max-width-12" type="number" ng-model="agg.settings.trimEdges" ng-change="onChangeInternal()">
 		</div>
 	</div>
-	<div class="tight-form-inner-box" ng-if="agg.type === 'terms'">
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 60px">
-					Order
-				</li>
-				<li>
-					<metric-segment-model property="agg.settings.order" options="orderOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+
+	<div ng-if="agg.type === 'terms'">
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label">Order</label>
+			<metric-segment-model property="agg.settings.order" options="orderOptions" on-change="onChangeInternal()" css-class="width-12"></metric-segment-model>
 		</div>
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 60px">
-					Size
-				</li>
-				<li>
-					<metric-segment-model property="agg.settings.size" options="sizeOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label width-10">Size</label>
+			<metric-segment-model property="agg.settings.size" options="sizeOptions" on-change="onChangeInternal()" css-class="width-12"></metric-segment-model>
 		</div>
-		<div class="tight-form last">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 60px">
-					Order By
-				</li>
-				<li>
-					<metric-segment-model property="agg.settings.orderBy" options="orderByOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label width-10">Order By</label>
+			<metric-segment-model property="agg.settings.orderBy" options="orderByOptions" on-change="onChangeInternal()" css-class="width-12"></metric-segment-model>
 		</div>
 	</div>
-	<div class="tight-form-inner-box" ng-if="agg.type === 'filters'">
-		<div class="tight-form" ng-repeat="filter in agg.settings.filters" ng-class="{last: $last}">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 100px">
-					Query {{$index + 1}}
-				</li>
-				<li>
-					<input type="text" class="tight-form-input input-large" ng-model="filter.query" spellcheck='false' placeholder="Lucene query" ng-blur="onChangeInternal()">
-				</li>
-				<li class="tight-form-item last" ng-if="$first">
+
+	<div ng-if="agg.type === 'filters'">
+		<div class="gf-form-inline" ng-repeat="filter in agg.settings.filters" ng-class="{last: $last}">
+			<div class="gf-form">
+				<label class="gf-form-item width-10">Query {{$index + 1}}</label>
+				<input type="text" class="gf-form-input max-width-12" ng-model="filter.query" spellcheck='false' placeholder="Lucene query" ng-blur="onChangeInternal()">
+			</div>
+			<div class="gf-form">
+				<label class="gf-form-label" ng-if="$first">
 					<a class="pointer" ng-click="addFiltersQuery()"><i class="fa fa-plus"></i></a>
-				</li>
-				<li class="tight-form-item last" ng-if="!$first">
+				</label>
+				<label class="gf-form-label" ng-if="!$first">
 					<a class="pointer" ng-click="removeFiltersQuery(filter)"><i class="fa fa-minus"></i></a>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+				</label>
+			</div>
 		</div>
-
 	</div>
 
 </div>

+ 61 - 117
public/app/plugins/datasource/elasticsearch/partials/metric_agg.html

@@ -1,138 +1,82 @@
-<div class="tight-form" ng-class="{'tight-form-disabled': agg.hide}">
-	<ul class="tight-form-list">
-		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
+<div class="gf-form-inline" ng-class="{'gf-form-disabled': agg.hide}">
+	<div class="gf-form">
+		<label class="gf-form-label query-keyword width-7">
 			Metric
 			&nbsp;
 			<a ng-click="toggleShowMetric()" bs-tooltip="'Click to toggle show / hide metric'">
 				<i class="fa fa-eye" ng-hide="agg.hide"></i>
 				<i class="fa fa-eye-slash" ng-show="agg.hide"></i>
 			</a>
-		</li>
-		<li>
-			<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
-		</li>
-		<li ng-if="aggDef.requiresField">
-			<metric-segment-model property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment-model>
-		</li>
-		<li ng-if="aggDef.isPipelineAgg">
-			<metric-segment-model property="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="tight-form-item-xxlarge"></metric-segment-model>
-		</li>
-		<li class="tight-form-item last" ng-if="settingsLinkText">
-			<a ng-click="toggleOptions()">
+		</label>
+	</div>
+
+	<div class="gf-form">
+		<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="width-10"></metric-segment-model>
+		<metric-segment-model ng-if="aggDef.requiresField" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment-model>
+		<metric-segment-model ng-if="aggDef.isPipelineAgg" property="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="width-12"></metric-segment-model>
+	</div>
+
+	<div class="gf-form gf-form--grow">
+		<label class="gf-form-label gf-form-label--grow">
+			<a ng-click="toggleOptions()" ng-if="settingsLinkText">
 				<i class="fa fa-caret-down" ng-show="showOptions"></i>
 				<i class="fa fa-caret-right" ng-hide="showOptions"></i>
-				{{settingsLinkText}}
+					{{settingsLinkText}}
 			</a>
-		</li>
-	</ul>
+		</label>
+	</div>
 
-	<ul class="tight-form-list pull-right">
-		<li class="tight-form-item last" ng-if="isFirst">
+	<div class="gf-form">
+		<label class="gf-form-label" ng-if="isFirst">
 			<a class="pointer" ng-click="addMetricAgg()"><i class="fa fa-plus"></i></a>
-		</li>
-		<li class="tight-form-item last" ng-if="!isSingle">
+		</label>
+		<label class="gf-form-label" ng-if="!isSingle">
 			<a class="pointer" ng-click="removeMetricAgg()"><i class="fa fa-minus"></i></a>
-		</li>
-	</ul>
-	<div class="clearfix"></div>
+		</label>
+	</div>
 </div>
 
-<div class="tight-form" ng-if="showOptions">
-	<div class="tight-form-inner-box tight-form-container">
-		<div class="tight-form" ng-if="agg.type === 'derivative'">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 75px;">
-					Unit
-				</li>
-				<li>
-					<input type="text" class="input-medium tight-form-input last" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
+<div class="gf-form-group" ng-if="showOptions">
 
-		<div class="tight-form" ng-if="agg.type === 'moving_avg'">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 75px;">
-					Window
-				</li>
-				<li>
-					<input type="number" class="input-medium tight-form-input last" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form" ng-if="agg.type === 'moving_avg'">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 75px;">
-					Model
-				</li>
-				<li>
-					<input type="text" class="input-medium tight-form-input last" ng-change="onChangeInternal()" ng-model="agg.settings.model" blur="onChange()" spellcheck='false'>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form last" ng-if="agg.type === 'percentiles'">
-			<ul class="tight-form-list">
-				<li class="tight-form-item">
-					Percentiles
-				</li>
-				<li>
-					<input type="text" class="input-xlarge tight-form-input last" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div ng-if="agg.type === 'extended_stats'">
-			<div class="tight-form" ng-repeat="stat in extendedStats">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						{{stat.text}}
-					</li>
-					<li class="tight-form-item last">
-						<editor-checkbox text="" model="agg.meta.{{stat.value}}" change="onChange()"></editor-checkbox>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-		</div>
-		<div class="tight-form" ng-if="agg.type === 'extended_stats'">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 100px">
-					Sigma
-				</li>
-				<li>
-					<input type="number" class="input-mini tight-form-input last" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
+	<div class="gf-form offset-width-7" ng-if="agg.type === 'derivative'">
+		<label class="gf-form-label width-10">Unit</label>
+		<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
+	</div>
 
-		<div class="tight-form" ng-if="aggDef.supportsInlineScript">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 100px;">
-					Script
-				</li>
-				<li>
-					<input type="text" class="input-medium tight-form-input last" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
+	<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
+		<label class="gf-form-label width-10">Window</label>
+		<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
+	</div>
 
-		<div class="tight-form" ng-if="aggDef.supportsMissing">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 100px;">
-					Missing
-					<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>
-				</li>
-				<li>
-					<input type="number" class="input-medium tight-form-input last" empty-to-null ng-model="agg.settings.missing" ng-blur="onChangeInternal()" spellcheck='false'>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+	<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
+		<label class="gf-form-label width-10">Model</label>
+		<input type="text" class="gf-form-input max-width-12" ng-change="onChangeInternal()" ng-model="agg.settings.model" blur="onChange()" spellcheck='false'>
+	</div>
+
+	<div class="gf-form offset-width-7" ng-if="agg.type === 'percentiles'">
+		<label class="gf-form-label width-10">Percentiles</label>
+		<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
+	</div>
+
+	<div ng-if="agg.type === 'extended_stats'">
+		<gf-form-switch ng-repeat="stat in extendedStats" class="gf-form offset-width-7" label="{{stat.text}}" label-class="width-10" checked="agg.meta[stat.value]" on-change="onChangeInternal()"></gf-form-switch>
+
+		<div class="gf-form offset-width-7">
+			<label class="gf-form-label width-10">Sigma</label>
+			<input type="number" class="gf-form-input max-width-12" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
 		</div>
+	</div>
+
+	<div class="gf-form offset-width-7" ng-if="aggDef.supportsInlineScript">
+		<label class="gf-form-label width-10">Script</label>
+		<input type="text" class="gf-form-input max-width-12" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
+	</div>
 
+	<div class="gf-form offset-width-7" ng-if="aggDef.supportsMissing">
+		<label class="gf-form-label width-10">
+			Missing
+			<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>
+		</label>
+		<input type="number" class="gf-form-input max-width-12" empty-to-null ng-model="agg.settings.missing" ng-blur="onChangeInternal()" spellcheck='false'>
 	</div>
 </div>

+ 28 - 29
public/app/plugins/datasource/elasticsearch/partials/query.editor.html

@@ -1,32 +1,31 @@
-<query-editor-row ctrl="ctrl">
-	<li class="tight-form-item query-keyword" style="width: 75px">
-		Query
-	</li>
-	<li>
-		<input type="text" class="tight-form-input" style="width: 345px;" ng-model="ctrl.target.query" spellcheck='false' placeholder="Lucene query" ng-blur="ctrl.refresh()">
-	</li>
-	<li class="tight-form-item query-keyword">
-		Alias
-	</li>
-	<li>
-		<input type="text" class="tight-form-input" style="width: 200px;" ng-model="ctrl.target.alias" spellcheck='false' placeholder="alias patterns (empty = auto)" ng-blur="ctrl.refresh()">
-	</li>
-</query-editor-row>
+<query-editor-row query-ctrl="ctrl" can-collapse="true">
+
+	<div class="gf-form-inline">
+		<div class="gf-form gf-form--grow">
+			<label class="gf-form-label query-keyword width-7">Query</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck='false' placeholder="Lucene query" ng-blur="ctrl.refresh()">
+		</div>
+		<div class="gf-form max-width-15">
+			<label class="gf-form-label query-keyword">Alias</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="alias patterns" ng-blur="ctrl.refresh()">
+		</div>
+	</div>
 
-<div ng-repeat="agg in ctrl.target.metrics">
-	<elastic-metric-agg
-		target="ctrl.target" index="$index"
-		get-fields="ctrl.getFields($fieldType)"
-		on-change="ctrl.queryUpdated()"
-		es-version="ctrl.esVersion">
-	</elastic-metric-agg>
-</div>
+	<div ng-repeat="agg in ctrl.target.metrics">
+		<elastic-metric-agg
+			target="ctrl.target" index="$index"
+			get-fields="ctrl.getFields($fieldType)"
+			on-change="ctrl.queryUpdated()"
+			es-version="ctrl.esVersion">
+		</elastic-metric-agg>
+	</div>
 
-<div ng-repeat="agg in ctrl.target.bucketAggs">
-	<elastic-bucket-agg
-		target="ctrl.target" index="$index"
-		get-fields="ctrl.getFields($fieldType)"
-		on-change="ctrl.queryUpdated()">
-	</elastic-bucket-agg>
-</div>
+	<div ng-repeat="agg in ctrl.target.bucketAggs">
+		<elastic-bucket-agg
+			target="ctrl.target" index="$index"
+			get-fields="ctrl.getFields($fieldType)"
+			on-change="ctrl.queryUpdated()">
+		</elastic-bucket-agg>
+	</div>
 
+</query-editor-row>

+ 43 - 0
public/app/plugins/datasource/elasticsearch/query_ctrl.ts

@@ -5,6 +5,7 @@ import './metric_agg';
 
 import angular from 'angular';
 import _ from 'lodash';
+import queryDef from './query_def';
 import {QueryCtrl} from 'app/plugins/sdk';
 
 export class ElasticQueryCtrl extends QueryCtrl {
@@ -38,6 +39,48 @@ export class ElasticQueryCtrl extends QueryCtrl {
     this.$rootScope.appEvent('elastic-query-updated');
   }
 
+  getCollapsedText() {
+    var metricAggs = this.target.metrics;
+    var bucketAggs = this.target.bucketAggs;
+    var metricAggTypes = queryDef.getMetricAggTypes(this.esVersion);
+    var bucketAggTypes = queryDef.bucketAggTypes;
+    var text = '';
+
+    if (this.target.query) {
+      text += 'Query: ' + this.target.query + ', ';
+    }
+
+    text += 'Metrics: ';
+
+    _.each(metricAggs, (metric, index) => {
+      var aggDef = _.findWhere(metricAggTypes, {value: metric.type});
+      text += aggDef.text + '(';
+      if (aggDef.requiresField) {
+        text += metric.field;
+      }
+      text += '), ';
+    });
+
+    _.each(bucketAggs, (bucketAgg, index) => {
+      if (index === 0) {
+        text += ' Group by: ';
+      }
+
+      var aggDef = _.findWhere(bucketAggTypes, {value: bucketAgg.type});
+      text += aggDef.text + '(';
+      if (aggDef.requiresField) {
+        text += bucketAgg.field;
+      }
+      text += '), ';
+    });
+
+    if (this.target.alias) {
+      text += 'Alias: ' + this.target.alias;
+    }
+
+    return text;
+  }
+
   handleQueryError(err) {
     this.error = err.message || 'Failed to issue metric query';
     return [];

+ 2 - 2
public/app/plugins/datasource/elasticsearch/query_def.js

@@ -20,9 +20,9 @@ function (_) {
     ],
 
     bucketAggTypes: [
-      {text: "Terms",           value: 'terms' },
+      {text: "Terms",           value: 'terms', requiresField: true},
       {text: "Filters",         value: 'filters' },
-      {text: "Date Histogram",  value: 'date_histogram' },
+      {text: "Date Histogram",  value: 'date_histogram', requiresField: true},
     ],
 
     orderByOptions: [

+ 6 - 4
public/app/plugins/datasource/grafana/partials/query.editor.html

@@ -1,5 +1,7 @@
-<query-editor-row ctrl="ctrl">
-	<li class="tight-form-item">
-		Test metric (fake data source)
-	</li>
+<query-editor-row query-ctrl="ctrl" can-collapse="false">
+	<div class="gf-form-inline">
+		<div class="gf-form">
+			<label class="gf-form-label">Test metric (fake data source)</label>
+		</div>
+	</div>
 </query-editor-row>

+ 2 - 2
public/app/plugins/datasource/graphite/add_graphite_func.js

@@ -11,10 +11,10 @@ function (angular, _, $, gfunc) {
     .module('grafana.directives')
     .directive('graphiteAddFunc', function($compile) {
       var inputTemplate = '<input type="text"'+
-                            ' class="tight-form-input input-medium tight-form-input"' +
+                            ' class="gf-form-input"' +
                             ' spellcheck="false" style="display:none"></input>';
 
-      var buttonTemplate = '<a  class="tight-form-item tight-form-func dropdown-toggle"' +
+      var buttonTemplate = '<a  class="gf-form-label query-part dropdown-toggle"' +
                               ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' +
                               '<i class="fa fa-plus"></i></a>';
 

+ 21 - 15
public/app/plugins/datasource/graphite/partials/query.editor.html

@@ -1,21 +1,27 @@
-<query-editor-row ctrl="ctrl">
+<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
 
-	<li class="tight-form-flex-wrapper" ng-show="ctrl.target.textEditor">
-		<input type="text" class="tight-form-clear-input" style="width: 100%;" ng-model="ctrl.target.target" give-focus="ctrl.target.textEditor" spellcheck='false' ng-model-onblur ng-change="ctrl.targetTextChanged()"></input>
-	</li>
+	<div class="gf-form" ng-show="ctrl.target.textEditor">
+		<input type="text" class="gf-form-input" ng-model="ctrl.target.target" spellcheck="false" ng-blur="ctrl.refresh()"></input>
+	</div>
 
-	<li ng-hide-start="ctrl.target.textEditor"></li>
+  <div ng-hide="ctrl.target.textEditor">
+		<div class="gf-form-inline">
+      <div ng-repeat="segment in ctrl.segments" role="menuitem" class="gf-form">
+        <metric-segment segment="segment" get-options="ctrl.getAltSegments($index)" on-change="ctrl.segmentValueChanged(segment, $index)"></metric-segment>
+      </div>
 
-	<li ng-repeat="segment in ctrl.segments" role="menuitem">
-		<metric-segment segment="segment" get-options="ctrl.getAltSegments($index)" on-change="ctrl.segmentValueChanged(segment, $index)"></metric-segment>
-	</li>
-	<li ng-repeat="func in ctrl.functions">
-		<span graphite-func-editor class="tight-form-item tight-form-func">
-		</span>
-	</li>
-	<li class="dropdown" graphite-add-func>
-	</li>
+      <div ng-repeat="func in ctrl.functions" class="gf-form">
+        <span graphite-func-editor class="gf-form-label query-part"></span>
+      </div>
 
-	<li ng-hide-end></li>
+      <div class="gf-form dropdown">
+        <span graphite-add-func></span>
+      </div>
+
+      <div class="gf-form gf-form--grow">
+				<div class="gf-form-label gf-form-label--grow"></div>
+			</div>
+    </div>
+  </div>
 
 </query-editor-row>

+ 1 - 5
public/app/plugins/datasource/influxdb/influx_query.ts

@@ -164,7 +164,7 @@ export default class InfluxQuery {
 
   getMeasurementAndPolicy(interpolate) {
     var policy = this.target.policy;
-    var measurement = this.target.measurement;
+    var measurement = this.target.measurement || 'measurement';
 
     if (!measurement.match('^/.*/')) {
       measurement = '"' + measurement+ '"';
@@ -192,10 +192,6 @@ export default class InfluxQuery {
       }
     }
 
-    if (!target.measurement) {
-      throw {message: "Metric measurement is missing"};
-    }
-
     var query = 'SELECT ';
     var i, y;
     for (i = 0; i < this.selectModels.length; i++) {

+ 86 - 63
public/app/plugins/datasource/influxdb/partials/query.editor.html

@@ -1,73 +1,96 @@
-<query-editor-row ctrl="ctrl">
-		<ul class="tight-form-list" ng-hide="ctrl.target.rawQuery">
-			<li class="tight-form-item query-keyword" style="width: 75px">
-				FROM
-			</li>
-			<li>
+<query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
+
+	<div class="gf-form" ng-if="ctrl.target.rawQuery">
+		<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" ng-blur="ctrl.refresh()"></input>
+	</div>
+
+	<div ng-if="!ctrl.target.rawQuery">
+
+		<div class="gf-form-inline">
+			<div class="gf-form">
+				<label class="gf-form-label query-keyword width-7">FROM</label>
+
 				<metric-segment segment="ctrl.policySegment" get-options="ctrl.getPolicySegments()" on-change="ctrl.policyChanged()"></metric-segment>
-			</li>
-			<li>
 				<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements()" on-change="ctrl.measurementChanged()"></metric-segment>
-			</li>
-			<li class="tight-form-item query-keyword" style="padding-left: 15px; padding-right: 15px;">
-				WHERE
-			</li>
-			<li ng-repeat="segment in ctrl.tagSegments">
+			</div>
+
+			<div class="gf-form">
+				<label class="gf-form-label query-keyword">WHERE</label>
+			</div>
+
+			<div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
 				<metric-segment segment="segment" get-options="ctrl.getTagsOrValues(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment>
-			</li>
-		</ul>
+			</div>
 
-		<div class="tight-form-flex-wrapper" ng-show="ctrl.target.rawQuery">
-			<input type="text" class="tight-form-clear-input" ng-model="ctrl.target.query" spellcheck="false" style="width: 100%;" ng-blur="ctrl.refresh()"></input>
+			<div class="gf-form gf-form--grow">
+				<div class="gf-form-label gf-form-label--grow"></div>
+			</div>
+		</div>
+
+		<div class="gf-form-inline" ng-repeat="selectParts in ctrl.queryModel.selectModels">
+			<div class="gf-form">
+				<label class="gf-form-label query-keyword width-7">
+					<span ng-show="$index === 0">SELECT</span>
+				</label>
+			</div>
+
+			<div class="gf-form" ng-repeat="part in selectParts">
+				<influx-query-part-editor
+														class="gf-form-label query-part"
+														part="part"
+														remove-action="ctrl.removeSelectPart(selectParts, part)"
+														part-updated="ctrl.selectPartUpdated(selectParts, part)"
+														get-options="ctrl.getPartOptions(part)">
+				</influx-query-part-editor>
+			</div>
+
+			<div class="gf-form">
+				<label class="dropdown"
+								dropdown-typeahead="ctrl.selectMenu"
+								dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
+				</label>
+			</div>
+
+			<div class="gf-form gf-form--grow">
+				<div class="gf-form-label gf-form-label--grow"></div>
+			</div>
+		</div>
+
+		<div class="gf-form-inline">
+			<div class="gf-form">
+				<label class="gf-form-label query-keyword width-7">
+					<span>GROUP BY</span>
+				</label>
+
+				<influx-query-part-editor
+								ng-repeat="part in ctrl.queryModel.groupByParts"
+								part="part"
+								class="gf-form-label query-part"
+								remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
+				</influx-query-part-editor>
+			</div>
+
+			<div class="gf-form gf-form--grow">
+				<div class="gf-form-label gf-form-label--grow"></div>
+			</div>
 		</div>
-</query-editor-row>
 
-<div ng-hide="ctrl.target.rawQuery">
-	<div class="tight-form" ng-repeat="selectParts in ctrl.queryModel.selectModels">
-		<ul class="tight-form-list">
-			<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
-				<span ng-show="$index === 0">SELECT</span>
-			</li>
-			<li ng-repeat="part in selectParts">
-				<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="ctrl.removeSelectPart(selectParts, part)" part-updated="ctrl.selectPartUpdated(selectParts, part)" get-options="ctrl.getPartOptions(part)"></influx-query-part-editor>
-			</li>
-			<li class="dropdown" dropdown-typeahead="ctrl.selectMenu" dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
-			</li>
-		</ul>
-		<div class="clearfix"></div>
 	</div>
 
-	<div class="tight-form">
-		<ul class="tight-form-list">
-			<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
-				<span>GROUP BY</span>
-			</li>
-			<li ng-repeat="part in ctrl.queryModel.groupByParts">
-				<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)"></influx-query-part-editor>
-			</li>
-			<li>
-				<metric-segment segment="ctrl.groupBySegment" get-options="ctrl.getGroupByOptions()" on-change="ctrl.groupByAction(part, $index)"></metric-segment>
-			</li>
-		</ul>
-		<div class="clearfix"></div>
+	<div class="gf-form-inline">
+		<div class="gf-form max-width-30">
+			<label class="gf-form-label query-keyword width-7">ALIAS BY</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label">Format as</label>
+			<div class="gf-form-select-wrapper">
+				<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
+			</div>
+		</div>
+		<div class="gf-form gf-form--grow">
+			<div class="gf-form-label gf-form-label--grow"></div>
+		</div>
 	</div>
-</div>
-
-<div class="tight-form">
-	<ul class="tight-form-list">
-		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
-			ALIAS BY
-		</li>
-		<li>
-			<input type="text" class="tight-form-clear-input input-xlarge" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
-		</li>
-		<li class="tight-form-item">
-			Format as
-		</li>
-		<li>
-			<select class="input-small tight-form-input" style="width: 104px" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
-		</li>
-	</ul>
-	<div class="clearfix"></div>
-</div>
 
+</query-editor-row>

+ 1 - 1
public/app/plugins/datasource/influxdb/partials/query.options.html

@@ -38,7 +38,7 @@
 </section>
 
 <div class="editor-row">
-	<div class="pull-left" style="margin-top: 30px;">
+	<div class="pull-left">
 
 		<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 1">
 			<h5>Alias patterns</h5>

+ 1 - 1
public/app/plugins/datasource/influxdb/partials/query_part.html

@@ -2,4 +2,4 @@
 	<span class="pointer fa fa-remove" ng-click="removeActionInternal()" ></span>
 </div>
 
-<a ng-click="toggleControls()">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>
+<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>

+ 10 - 1
public/app/plugins/datasource/influxdb/query_ctrl.ts

@@ -23,6 +23,7 @@ export class InfluxQueryCtrl extends QueryCtrl {
   measurementSegment: any;
   removeTagFilterSegment: any;
 
+
   /** @ngInject **/
   constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) {
     super($scope, $injector);
@@ -154,7 +155,11 @@ export class InfluxQueryCtrl extends QueryCtrl {
   }
 
   toggleEditorMode() {
-    this.target.query = this.queryModel.render(false);
+    try {
+      this.target.query = this.queryModel.render(false);
+    } catch (err) {
+      console.log('query render error');
+    }
     this.target.rawQuery = !this.target.rawQuery;
   }
 
@@ -316,5 +321,9 @@ export class InfluxQueryCtrl extends QueryCtrl {
       return '=';
     }
   }
+
+  getCollapsedText() {
+    return this.queryModel.render(false);
+  }
 }
 

+ 250 - 220
public/app/plugins/datasource/opentsdb/partials/query.editor.html

@@ -1,223 +1,253 @@
-<query-editor-row ctrl="ctrl">
-	<li class="tight-form-item query-keyword" style="width: 100px">
-		Metric
-	</li>
-	<li>
-		<input type="text" class="input-large tight-form-input" ng-model="ctrl.target.metric"
-		spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
-		ng-blur="ctrl.targetBlur()">
-		</input>
-		<a bs-tooltip="ctrl.errors.metric" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.metric">
-			<i class="fa fa-warning"></i>
-		</a>
-	</li>
-	<li class="tight-form-item query-keyword">
-		Aggregator
-	</li>
-	<li>
-		<select ng-model="ctrl.target.aggregator" class="tight-form-input input-small"
-			ng-options="agg for agg in ctrl.aggregators"
-			ng-change="ctrl.targetBlur()">
-		</select>
-		<a bs-tooltip="ctrl.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.aggregator">
-			<i class="fa fa-warning"></i>
-		</a>
-	</li>
-
-	<li class="tight-form-item query-keyword">
-		Alias:
-		<tip>Use patterns like $tag_tagname to replace part of the alias for a tag value</tip>
-	</li>
-	<li>
-		<input type="text" class="tight-form-input input-large"
-		ng-model="ctrl.target.alias"
-		spellcheck='false'
-		placeholder="series alias"
-		data-min-length=0 data-items=100
-		ng-blur="ctrl.targetBlur()"></input>
-	</li>
-</query-editor-row>
+<query-editor-row query-ctrl="ctrl" can-collapse="false">
+	<div class="gf-form-inline">
+		<div class="gf-form max-width-25">
+			<label class="gf-form-label query-keyword width-8">
+				Metric
+				<label class="gf-form-label" bs-tooltip="ctrl.errors.metric" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.metric">
+					<i class="fa fa-warning"></i>
+				</label>
+			</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.metric"
+						 spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
+						ng-blur="ctrl.targetBlur()">
+			</input>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label query-keyword">
+				Aggregator
+				<a bs-tooltip="ctrl.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.aggregator">
+					<i class="fa fa-warning"></i>
+				</a>
+			</label>
+			<div class="gf-form-select-wrapper max-width-15">
+				<select ng-model="ctrl.target.aggregator" class="gf-form-input"
+								ng-options="agg for agg in ctrl.aggregators"
+								ng-change="ctrl.targetBlur()">
+		 	 </select>
+			</div>
+		</div>
+			<div class="gf-form max-width-20">
+				<label class="gf-form-label query-keyword width-6">
+					Alias:
+					<info-popover mode="right-normal">
+						Use patterns like $tag_tagname to replace part of the alias for a tag value
+					</info-popover>
+				</label>
+				<input  type="text" class="gf-form-input"
+		   					ng-model="ctrl.target.alias"
+								spellcheck='false'
+								placeholder="series alias"
+								data-min-length=0 data-items=100
+								ng-blur="ctrl.targetBlur()"></input>
+			</div>
+
+			<div class="gf-form gf-form--grow">
+				<div class="gf-form-label gf-form-label--grow"></div>
+			</div>
+	</div>
+
+	<div class="gf-form-inline">
+		<div class="gf-form max-width-25">
+			<label class="gf-form-label query-keyword width-8">Down sample</label>
+			<input type="text" class="gf-form-input"
+						 ng-model="ctrl.target.downsampleInterval"
+						 ng-model-onblur
+			       ng-change="ctrl.targetBlur()"
+			       placeholder="interval"></input>
+			<info-popover mode="right-absolute">
+				blank for auto, or for example <code>1m</code>
+			</info-popover>
+		</div>
+
+		<div class="gf-form">
+			<label class="gf-form-label query-keyword">Aggregator</label>
+			<div class="gf-form-select-wrapper">
+				<select ng-model="ctrl.target.downsampleAggregator" class="gf-form-input"
+								ng-options="agg for agg in ctrl.aggregators"
+				        ng-change="ctrl.targetBlur()">
+				</select>
+			</div>
+		</div>
+
+		<div class="gf-form" ng-if="ctrl.tsdbVersion == 2">
+			<label class="gf-form-label query-keyword width-6">Fill</label>
+			<div class="gf-form-select-wrapper">
+				<select ng-model="ctrl.target.downsampleFillPolicy" class="gf-form-input"
+								ng-options="agg for agg in ctrl.fillPolicies"
+								ng-change="ctrl.targetBlur()">
+				</select>
+			</div>
+		</div>
+
+		<gf-form-switch class="gf-form"
+										label="Disable downsampling"
+										checked="ctrl.target.disableDownsampling"
+										on-change="ctrl.targetBlur()">
+		</gf-form-switch>
+
+		<div class="gf-form gf-form--grow">
+			<div class="gf-form-label gf-form-label--grow"></div>
+		</div>
+	</div>
+
+	<div class="gf-form-inline" ng-if="ctrl.tsdbVersion == 2">
+		<div class="gf-form">
+
+			<label class="gf-form-label query-keyword width-8">
+				Filters
+				<info-popover mode="right-normal">
+					Filters does not work with tags, either of the two will work but not both.
+				</info-popover>
+			</label>
+
+			<div ng-repeat="fil in ctrl.target.filters track by $index" class="gf-form-label">
+				{{fil.tagk}}&nbsp;=&nbsp;{{fil.type}}&#40;{{fil.filter}}&#41;&nbsp;&#44&nbsp;groupBy&nbsp;=&nbsp;{{fil.groupBy}}
+				<a ng-click="ctrl.editFilter(fil, $index)">
+					<i class="fa fa-pencil"></i>
+				</a>
+				<a ng-click="ctrl.removeFilter($index)">
+					<i class="fa fa-remove"></i>
+				</a>
+			</div>
+			<label class="gf-form-label query-keyword" ng-hide="ctrl.addFilterMode">
+				<a ng-click="ctrl.addFilter()">
+					<i class="fa fa-plus"></i>
+				</a>
+			</label>
+ 		</div>
+
+		<div class="gf-form-inline" ng-show="ctrl.addFilterMode">
+			<div class="gf-form">
+				<input type="text" class="gf-form-input" spellcheck='false'
+						 bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
+             ng-model="ctrl.target.currentFilterKey" placeholder="key">
+				</input>
+			</div>
+
+			<div class="gf-form">
+				<label class="gf-form-label">Type</label>
+				<div class="gf-form-select-wrapper">
+					<select ng-model="ctrl.target.currentFilterType" class="gf-form-input" ng-options="filType for filType in ctrl.filterTypes">
+					</select>
+				</div>
+			</div>
+
+			<div class="gf-form">
+				<input type="text" class="gf-form-input" spellcheck='false' bs-typeahead="ctrl.suggestTagValues" data-min-length=0 data-items=100 ng-model="ctrl.target.currentFilterValue" placeholder="filter">
+				</input>
+			</div>
+
+			<gf-form-switch class="gf-form" label="Group by" checked="ctrl.target.currentFilterGroupBy" on-change="ctrl.targetBlur()">
+			</gf-form-switch>
+
+			<div class="gf-form" ng-show="ctrl.addFilterMode">
+				<label class="gf-form-label" ng-show="ctrl.errors.filters">
+					<a bs-tooltip="ctrl.errors.filters" style="color: rgb(229, 189, 28)" >
+						<i class="fa fa-warning"></i>
+					</a>
+				</label>
+
+				<label class="gf-form-label">
+					<a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">add filter</a>
+					<a ng-click="ctrl.closeAddFilterMode()">
+						<i class="fa fa-remove"></i>
+					</a>
+				</label>
+			</div>
+
+		</div>
+
+		<div class="gf-form gf-form--grow">
+			<div class="gf-form-label gf-form-label--grow"></div>
+		</div>
+	</div>
+
+	<div class="gf-form-inline">
+		<div class="gf-form">
+			<label class="gf-form-label query-keyword width-8">
+				Tags
+				<info-popover mode="right-normal" ng-if="ctrl.tsdbVersion == 2">
+					Please use filters, tags are deprecated in opentsdb 2.2
+				</info-popover>
+			</label>
+		</div>
+
+		<div class="gf-form" ng-repeat="(key, value) in ctrl.target.tags track by $index" class="gf-form">
+			<label class="gf-form-label">
+				{{key}}&nbsp;=&nbsp;{{value}}
+				<a ng-click="ctrl.editTag(key, value)">
+					<i class="fa fa-pencil"></i>
+				</a>
+				<a ng-click="ctrl.removeTag(key)">
+					<i class="fa fa-remove"></i>
+				</a>
+			</label>
+		</div>
+
+		<div class="gf-form" ng-hide="ctrl.addTagMode">
+			<label class="gf-form-label query-keyword">
+				<a ng-click="ctrl.addTag()"><i class="fa fa-plus"></i></a>
+			</label>
+		</div>
+
+		<div class="gf-form" ng-show="ctrl.addTagMode">
+			<input type="text"
+						 class="gf-form-input" spellcheck='false'
+						 bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
+					   ng-model="ctrl.target.currentTagKey" placeholder="key">
+			</input>
+
+			<input type="text" class="gf-form-input"
+						 spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
+						 data-min-length=0 data-items=100 ng-model="ctrl.target.currentTagValue" placeholder="value">
+			</input>
 
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
-			Down sample
-		</li>
-
-		<li>
-			<input type="text" class="input-large tight-form-input"
-			ng-model="ctrl.target.downsampleInterval"
-			ng-model-onblur
-			ng-change="ctrl.targetBlur()"
-			placeholder="interval (empty = auto)"></input>
-		</li>
-
-		<li class="tight-form-item query-keyword">
-			Aggregator
-		</li>
-
-		<li>
-			<select ng-model="ctrl.target.downsampleAggregator" class="tight-form-input input-small"
-				ng-options="agg for agg in ctrl.aggregators"
-				ng-change="ctrl.targetBlur()">
-			</select>
-		</li>
-
-		<li class="tight-form-item query-keyword" style="width: 59px" ng-if="ctrl.tsdbVersion == 2">
-			Fill
-		</li>
-
-		<li ng-if="ctrl.tsdbVersion == 2">
-			<select ng-model="ctrl.target.downsampleFillPolicy" class="tight-form-input input-small"
-				ng-options="agg for agg in ctrl.fillPolicies"
-				ng-change="ctrl.targetBlur()">
-			</select>
-		</li>
-
-		<li class="tight-form-item query-keyword">
-			Disable downsampling <editor-checkbox text="" model="ctrl.target.disableDownsampling" change="ctrl.targetBlur()"></editor-checkbox>
-		</li>
-
-	</ul>
-	<div class="clearfix"></div>
-</div>
-
-<div class="tight-form" ng-if="ctrl.tsdbVersion == 2">
-  <ul class="tight-form-list" role="menu">
-    <li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
-      Filters
-      <tip ng-if="ctrl.tsdbVersion == 2">Filters does not work with tags, either of the two will work but not both.</tip>
-    </li>
-    <li ng-repeat="fil in ctrl.target.filters track by $index" class="tight-form-item">
-      {{fil.tagk}}&nbsp;=&nbsp;{{fil.type}}&#40;{{fil.filter}}&#41;&nbsp;&#44&nbsp;groupBy&nbsp;=&nbsp;{{fil.groupBy}}
-      <a ng-click="ctrl.editFilter(fil, $index)">
-        <i class="fa fa-pencil"></i>
-      </a>
-      <a ng-click="ctrl.removeFilter($index)">
-        <i class="fa fa-remove"></i>
-      </a>
-    </li>
-    <li class="tight-form-item query-keyword" ng-hide="ctrl.addFilterMode">
-      <a ng-click="ctrl.addFilter()">
-        <i class="fa fa-plus"></i>
-      </a>
-    </li>
-
-    <li class="query-keyword" ng-show="ctrl.addFilterMode">
-      <input type="text" class="input-small tight-form-input" spellcheck='false'
-      bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
-      ng-model="ctrl.target.currentFilterKey" placeholder="key"></input>
-
-      Type <select ng-model="ctrl.target.currentFilterType"
-      class="tight-form-input input-small"
-      ng-options="filType for filType in ctrl.filterTypes">
-      </select>
- 
-      <input type="text" class="input-small tight-form-input"
-      spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
-      data-min-length=0 data-items=100 ng-model="ctrl.target.currentFilterValue" placeholder="filter">
-      </input>
-
-      groupBy <editor-checkbox text="" model="ctrl.target.currentFilterGroupBy"></editor-checkbox>
-
-      <a bs-tooltip="ctrl.errors.filters"
-        style="color: rgb(229, 189, 28)"
-        ng-show="ctrl.errors.filters">
-        <i class="fa fa-warning"></i>
-      </a>
- 
-      <a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">
-        add filter
-      </a>
-      <a ng-click="ctrl.closeAddFilterMode()">
-        <i class="fa fa-remove"></i>
-      </a>
-
-    </li>
-  </ul>
-  <div class="clearfix"></div>
-</div>
-
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
-			Tags
-      <tip ng-if="ctrl.tsdbVersion == 2">Please use filters, tags are deprecated in opentsdb 2.2</tip>
-		</li>
-		<li ng-repeat="(key, value) in ctrl.target.tags track by $index" class="tight-form-item">
-			{{key}}&nbsp;=&nbsp;{{value}}
-			<a ng-click="ctrl.editTag(key, value)">
-				<i class="fa fa-pencil"></i>
-			</a>
-			<a ng-click="ctrl.removeTag(key)">
-				<i class="fa fa-remove"></i>
-			</a>
-		</li>
-
-		<li class="tight-form-item query-keyword" ng-hide="ctrl.addTagMode">
-			<a ng-click="ctrl.addTag()">
-				<i class="fa fa-plus"></i>
-			</a>
-		</li>
-
-		<li ng-show="ctrl.addTagMode">
-			<input type="text" class="input-small tight-form-input" spellcheck='false'
-			bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
-			ng-model="ctrl.target.currentTagKey" placeholder="key"></input>
-
-			<input type="text" class="input-small tight-form-input"
-			spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
-			data-min-length=0 data-items=100 ng-model="ctrl.target.currentTagValue" placeholder="value">
+			<label class="gf-form-label" ng-show="ctrl.errors.tags">
+				<a bs-tooltip="ctrl.errors.tags" style="color: rgb(229, 189, 28)" >
+					<i class="fa fa-warning"></i>
+				</a>
+			</label>
+			<label class="gf-form-label" >
+				<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">add tag</a>
+				<a ng-click="ctrl.closeAddTagMode()"><i class="fa fa-remove"></i></a>
+			</label>
+		</div>
+
+		<div class="gf-form gf-form--grow">
+			<div class="gf-form-label gf-form-label--grow"></div>
+		</div>
+	</div>
+
+	<div class="gf-form-inline">
+		<gf-form-switch class="gf-form" label="Rate" label-class="width-8 query-keyword" checked="ctrl.target.shouldComputeRate" on-change="ctrl.targetBlur()">
+		</gf-form-switch>
+
+		<gf-form-switch ng-hide="!ctrl.target.shouldComputeRate"
+										class="gf-form" label="Counter" checked="ctrl.target.isCounter" on-change="ctrl.targetBlur()">
+		</gf-form-switch>
+
+
+		<div class="gf-form" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
+			<label class="gf-form-label">Counter Max</label>
+			<input type="text" class="gf-form-input"
+					 	 ng-disabled="!ctrl.target.shouldComputeRate"
+						 ng-model="ctrl.target.counterMax" spellcheck='false'
+						 placeholder="max value" ng-model-onblur
+						 ng-blur="ctrl.targetBlur()">
 			</input>
 
-      <a bs-tooltip="ctrl.errors.tags"
-        style="color: rgb(229, 189, 28)"
-        ng-show="ctrl.errors.tags">
-        <i class="fa fa-warning"></i>
-      </a>
-
-			<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">
-				add tag
-			</a>
-      <a ng-click="ctrl.closeAddTagMode()">
-        <i class="fa fa-remove"></i>
-      </a>
-		
-    </li>
-	</ul>
-	<div class="clearfix"></div>
-</div>
-
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
-			Rate <editor-checkbox text="" model="ctrl.target.shouldComputeRate" change="ctrl.targetBlur()"></editor-checkbox>
-		</li>
-
-		<li class="tight-form-item query-keyword" ng-hide="!ctrl.target.shouldComputeRate">
-			Counter <editor-checkbox text="" model="ctrl.target.isCounter" change="ctrl.targetBlur()"></editor-checkbox>
-		</li>
-
-		<li class="tight-form-item query-keyword" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
-			Counter Max:
-		</li>
-
-		<li ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
-			<input type="text" class="tight-form-input input-small" ng-disabled="!ctrl.target.shouldComputeRate"
-			ng-model="ctrl.target.counterMax" spellcheck='false'
-			placeholder="max value" ng-model-onblur
-			ng-blur="ctrl.targetBlur()"></input>
-		</li>
-		<li class="tight-form-item query-keyword" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
-			Reset Value:
-		</li>
-		<li ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
-			<input type="text" class="tight-form-input input-small" ng-disabled="!ctrl.target.shouldComputeRate"
-			ng-model="ctrl.target.counterResetValue" spellcheck='false'
-			placeholder="reset value" ng-model-onblur
-			ng-blur="ctrl.targetBlur()"></input>
-		</li>
-	</ul>
-
-	<div class="clearfix"></div>
-</div>
+			<label class="gf-form-label">Reset Value</label>
+			<input type="text" class="tight-form-input input-small"
+					   ng-disabled="!ctrl.target.shouldComputeRate"
+						 ng-model="ctrl.target.counterResetValue" spellcheck='false'
+						 placeholder="reset value" ng-model-onblur
+						 ng-blur="ctrl.targetBlur()">
+			</input>
+		</div>
+
+		<div class="gf-form gf-form--grow">
+			<div class="gf-form-label gf-form-label--grow"></div>
+		</div>
+	</div>
+</query-editor-row>
+

+ 48 - 74
public/app/plugins/datasource/prometheus/partials/query.editor.html

@@ -1,80 +1,54 @@
-<query-editor-row ctrl="ctrl">
-	<li class="tight-form-item" style="width: 94px">
-		Query
-	</li>
-	<li>
-		<input type="text"
-		class="input-xxlarge tight-form-input"
-		ng-model="ctrl.target.expr"
-		spellcheck='false'
-		placeholder="query expression"
-		data-min-length=0 data-items=100
-		ng-model-onblur
-		ng-change="ctrl.refreshMetricData()">
-	</li>
-	<li class="tight-form-item">
-		Metric
-	</li>
-	<li>
-		<input type="text"
-		class="input-medium tight-form-input"
-		ng-model="ctrl.target.metric"
-		spellcheck='false'
-		bs-typeahead="ctrl.suggestMetrics"
-		placeholder="metric name"
-		data-min-length=0 data-items=100>
-	</li>
-</query-editor-row>
+<query-editor-row query-ctrl="ctrl" can-collapse="false">
+	<div class="gf-form-inline">
+		<div class="gf-form gf-form--grow">
+			<label class="gf-form-label width-8">Query</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.expr" spellcheck='false' placeholder="query expression" data-min-length=0 data-items=100 ng-model-onblur ng-change="ctrl.refreshMetricData()">
+		</div>
+		<div class="gf-form max-width-22">
+			<label class="gf-form-label">Metric lookup</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.metric" spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100>
+		</div>
+	</div>
 
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item tight-form-align" style="width: 94px">
-			Legend format
-		</li>
-		<li>
-			<input type="text" class="tight-form-input input-xxlarge" ng-model="ctrl.target.legendFormat"
+	<div class="gf-form-inline">
+		<div class="gf-form max-width-26">
+			<label class="gf-form-label width-8">Legend format</label>
+			<input type="text" class="gf-form-input" ng-model="ctrl.target.legendFormat"
 			spellcheck='false' placeholder="legend format" data-min-length=0 data-items=1000
 			ng-model-onblur ng-change="ctrl.refreshMetricData()">
 			</input>
-		</li>
-	</ul>
-
-	<div class="clearfix"></div>
-</div>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label width-5">Step</label>
+			<input type="text" class="gf-form-input max-width-5" ng-model="ctrl.target.interval"
+					   data-placement="right"
+			       spellcheck='false'
+			       placeholder="{{ctrl.panelCtrl.interval}}"
+			       data-min-length=0 data-items=100
+			       ng-model-onblur
+			       ng-change="ctrl.refreshMetricData()"/>
+			<info-popover mode="right-absolute">
+				Leave blank for auto handling based on time range and panel width
+			</info-popover>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label">Resolution</label>
+			<div class="gf-form-select-wrapper max-width-15">
+				<select ng-model="ctrl.target.intervalFactor" class="gf-form-input"
+					ng-options="r.factor as r.label for r in ctrl.resolutions"
+					ng-change="ctrl.refreshMetricData()">
+				</select>
+			</div>
+			<label class="gf-form-label">
+				<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
+					<i class="fa fa-share-square-o"></i>
+				</a>
+			</label>
+		</div>
 
-<div class="tight-form">
-	<ul class="tight-form-list" role="menu">
-		<li class="tight-form-item tight-form-align" style="width: 94px">
-			Step
-		</li>
-		<li>
-			<input type="text" class="input-mini tight-form-input" ng-model="ctrl.target.interval"
-			bs-tooltip="'Leave blank for auto handling based on time range and panel width'"
-			data-placement="right"
-			spellcheck='false'
-			placeholder="{{ctrl.panelCtrl.interval}}"
-			data-min-length=0 data-items=100
-			ng-model-onblur
-			ng-change="ctrl.refreshMetricData()"
-			/>
-			</input>
-		</li>
+		<div class="gf-form gf-form--grow">
+			<div class="gf-form-label gf-form-label--grow"></div>
+		</div>
+	</div>
 
-		<li class="tight-form-item">
-			Resolution
-		</li>
-		<li>
-			<select ng-model="ctrl.target.intervalFactor" class="tight-form-input input-mini"
-				ng-options="r.factor as r.label for r in ctrl.resolutions"
-				ng-change="ctrl.refreshMetricData()">
-			</select>
-		</li>
-		<li class="tight-form-item">
-			<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
-				<i class="fa fa-share-square-o"></i>
-			</a>
-		</li>
-	</ul>
-
-	<div class="clearfix"></div>
-</div>
+</query-editor-row>

+ 1 - 0
public/sass/_grafana.scss

@@ -70,6 +70,7 @@
 @import "components/drop";
 @import "components/query_editor";
 @import "components/tabbed_view";
+@import "components/query_part";
 
 // PAGES
 @import "pages/login";

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

@@ -67,8 +67,8 @@ $page-gradient: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98%
 
 // Links
 // -------------------------
-$link-color:              darken($white,11%);
-$link-color-disabled:     darken($link-color,30%);
+$link-color:              darken($white, 11%);
+$link-color-disabled:     darken($link-color, 30%);
 $link-hover-color:        $white;
 $external-link-color:     $blue;
 
@@ -76,7 +76,7 @@ $external-link-color:     $blue;
 // -------------------------
 $headings-color:       darken($white,11%);
 $abbr-border-color: 	 $gray-3 !default;
-$text-muted: 			     darken($link-color,30%);
+$text-muted: 			     $text-color-weak;
 
 $blockquote-small-color:  $gray-3 !default;
 $blockquote-border-color: $gray-4 !default;

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

@@ -82,7 +82,7 @@ $external-link-color:    $blue;
 // -------------------------
 $headings-color:       $text-color;
 $abbr-border-color: 	 $gray-2 !default;
-$text-muted: 			     darken($link-color,30%);
+$text-muted: 			     $text-color-weak;
 
 $blockquote-small-color:  $gray-2 !default;
 $blockquote-border-color: $gray-3 !default;

+ 6 - 2
public/sass/base/_type.scss

@@ -27,9 +27,9 @@ em      { font-style: italic; color: $headings-color; }
 cite    { font-style: normal; }
 
 // Utility classes
-.muted               { color: $gray-2; }
+.muted               { color: $text-muted; }
 a.muted:hover,
-a.muted:focus        { color: darken($gray-2, 10%); }
+a.muted:focus        { color: darken($text-muted, 10%); }
 
 .text-warning        { color: $state-warning-text; }
 a.text-warning:hover,
@@ -118,6 +118,10 @@ small,
   font-weight: normal;
 }
 
+.small-xs {
+  font-size: $font-size-xs;
+}
+
 mark,
 .mark {
   padding: .2em;

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

@@ -1,4 +1,4 @@
-$popover-arrow-size: 1rem;
+$popover-arrow-size: 0.7rem;
 $color: inherit;
 $backgroundColor: $btn-secondary-bg;
 $color: $text-color;

+ 40 - 8
public/sass/components/_gf-form.scss

@@ -8,8 +8,22 @@ $gf-form-margin: 0.25rem;
   text-align: left;
   position: relative;
 
-  .cr1 {
-    margin-left: 8px;
+  &--offset-1 {
+    margin-left: $spacer;
+  }
+
+  &--grow {
+    flex-grow: 1;
+  }
+}
+
+.gf-form-disabled {
+  color: $text-color-weak;
+
+  .query-keyword,
+  a,
+  .gf-form-input {
+    color: $text-color-weak;
   }
 }
 
@@ -22,10 +36,6 @@ $gf-form-margin: 0.25rem;
   flex-direction: row;
   flex-wrap: wrap;
   align-content: flex-start;
-
-  .gf-form-flex {
-    flex-grow: 1;
-  }
 }
 
 .gf-form-button-row {
@@ -48,6 +58,12 @@ $gf-form-margin: 0.25rem;
 
   border: $input-btn-border-width solid transparent;
   @include border-radius($label-border-radius-sm);
+
+
+  &--grow {
+    flex-grow: 1;
+    min-height: 2.70rem;
+  }
 }
 
 .gf-form-checkbox {
@@ -107,6 +123,21 @@ $gf-form-margin: 0.25rem;
   }
 
   &.gf-size-auto { width: auto; }
+
+  &--dropdown {
+    padding-right: $input-padding-x*2;
+
+    &:after {
+      position: absolute;
+      top: 35%;
+      right: $input-padding-x/2;
+      background-color: transparent;
+      color: $input-color;
+      font: normal normal normal $font-size-sm/1 FontAwesome;
+      content: '\f0d7';
+      pointer-events: none;
+    }
+  }
 }
 
 .gf-form-select-wrapper {
@@ -152,9 +183,11 @@ $gf-form-margin: 0.25rem;
 }
 
 .gf-form-btn {
-  margin-right: $gf-form-margin;
   padding: $input-padding-y $input-padding-x;
+  margin-right: $gf-form-margin;
   line-height: $input-line-height;
+  font-size: $font-size-sm;
+
   flex-shrink: 0;
   flex-grow: 0;
 }
@@ -209,4 +242,3 @@ $gf-form-margin: 0.25rem;
 select.gf-form-input ~ .gf-form-help-icon {
   right: 10px;
 }
-

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

@@ -4,13 +4,66 @@
 }
 
 .query-segment-key {
-  border-right: none;
-  padding-right: 1px;
+  //border-right: none;
+  //padding-right: 1px;
 }
 
 .query-segment-operator {
-  padding-right: 1px;
-  border-right: none;
+  //padding-right: 1px;
+  //border-right: none;
   color: $orange;
 }
 
+.gf-form-query {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+  align-content: flex-start;
+  align-items: flex-start;
+
+  .gf-form,
+  .gf-form-filler {
+    margin-bottom: 2px;
+  }
+
+  .gf-form-switch,
+  .gf-form-switch label,
+  .gf-form-input,
+  .gf-form-select-wrapper,
+  .gf-form-filler,
+  .gf-form-label {
+    margin-right: 2px;
+  }
+}
+
+.gf-form-query-content {
+  flex-grow: 2;
+
+  &--collapsed {
+    overflow: hidden;
+
+    .gf-form-label {
+      overflow: hidden;
+      text-overflow: ellipsis;
+      width: 100%;
+      white-space: nowrap;
+    }
+  }
+}
+
+.gf-form-query-letter-cell {
+  .gf-form-query-letter-cell-carret {
+    display: inline-block;
+    width: 0.7rem;
+    position: relative;
+    left: -2px;
+  }
+  .gf-form-query-letter-cell-letter {
+    font-weight: bold;
+    color: $blue;
+  }
+  .gf-form-query-letter-cell-ds {
+    color: $text-color-weak;
+  }
+}
+

+ 11 - 0
public/sass/components/_query_part.scss

@@ -0,0 +1,11 @@
+
+.query-part {
+  background-color: $input-bg !important;
+
+  &.show-function-controls {
+    padding-top: 5px;
+    min-width: 100px;
+    text-align: center;
+  }
+}
+

+ 1 - 1
public/sass/mixins/_drop_element.scss

@@ -10,7 +10,7 @@
       font-family: inherit;
       background: $theme-bg;
       color: $theme-color;
-      padding: $spacer;
+      padding: 0.65rem;
       font-size: $font-size-sm;
       max-width: 20rem;
 

+ 2 - 1
public/sass/utils/_utils.scss

@@ -61,13 +61,14 @@ button.close {
 .hide {
   display: none;
 }
+
 .show {
   display: block;
 }
 
 // Visibility
 .invisible {
-  visibility: hidden;
+  visibility: hidden !important;
 }
 
 // For Affix plugin

+ 6 - 0
public/sass/utils/_widths.scss

@@ -17,3 +17,9 @@
   }
 }
 
+@for $i from 1 through 30 {
+  .offset-width-#{$i} {
+    margin-left: ($spacer * $i) !important;
+  }
+}
+