瀏覽代碼

poc for new metric segment

Torkel Ödegaard 9 年之前
父節點
當前提交
b65642564a

+ 13 - 0
public/app/core/components/form_dropdown/form_dropdown.html

@@ -0,0 +1,13 @@
+<input type="text"
+			 data-provide="typeahead"
+			 class="gf-form-input"
+			 spellcheck="false"
+			 style="display:none"></input>
+
+<a class="gf-form-label"
+	 ng-class="ctrl.cssClass"
+	 tabindex="1"
+	 ng-click="ctrl.open()"
+	 give-focus="ctrl.focus"
+	 ng-bind-html="ctrl.display"></a>
+

+ 182 - 0
public/app/core/components/form_dropdown/form_dropdown.ts

@@ -0,0 +1,182 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import config from 'app/core/config';
+import _ from 'lodash';
+import $ from 'jquery';
+import coreModule from '../../core_module';
+
+function typeaheadMatcher(item) {
+  var str = this.query;
+  if (str[0] === '/') { str = str.substring(1); }
+  if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); }
+  return item.toLowerCase().match(str.toLowerCase());
+}
+
+export class FormDropdownCtrl {
+  inputElement: any;
+  linkElement: any;
+  value: any;
+  text: any;
+  display: any;
+  options: any;
+  cssClass: any;
+  allowCustom: any;
+  linkMode: boolean;
+  cancelBlur: any;
+  onChange: any;
+
+  constructor(private $scope, $element, private $sce, private templateSrv) {
+    this.inputElement = $element.find('input').first();
+    this.linkElement = $element.find('a').first();
+    this.linkMode = true;
+    this.cancelBlur = null;
+
+    if (this.options) {
+      var item = _.find(this.options, {value: this.value});
+      this.updateDisplay(item ? item.text : this.value);
+    }
+
+    this.inputElement.attr('data-provide', 'typeahead');
+    this.inputElement.typeahead({
+      source: this.typeaheadSource.bind(this),
+      minLength: 0,
+      items: 10000,
+      updater: this.typeaheadUpdater.bind(this),
+      matcher: typeaheadMatcher,
+    });
+
+    // modify typeahead lookup
+    // this = typeahead
+    var typeahead = this.inputElement.data('typeahead');
+    typeahead.lookup = function () {
+      this.query = this.$element.val() || '';
+      var items = this.source(this.query, $.proxy(this.process, this));
+      return items ? this.process(items) : items;
+    };
+
+    this.linkElement.keydown(evt => {
+      // trigger typeahead on down arrow or enter key
+      if (evt.keyCode === 40 || evt.keyCode === 13) {
+        this.linkElement.click();
+      }
+    });
+
+    this.inputElement.blur(this.inputBlur.bind(this));
+  }
+
+  typeaheadSource(query, callback) {
+    if (this.options) {
+      var typeaheadOptions = _.map(this.options, 'text');
+
+      // add current custom value
+      if (this.allowCustom) {
+        if (_.indexOf(typeaheadOptions, this.text) === -1) {
+          typeaheadOptions.unshift(this.text);
+        }
+      }
+
+      callback(typeaheadOptions);
+    }
+  }
+
+  typeaheadUpdater(text) {
+    if (text === this.text) {
+      clearTimeout(this.cancelBlur);
+      this.inputElement.focus();
+      return text;
+    }
+
+    this.inputElement.val(text);
+    this.switchToLink(true);
+    return text;
+  }
+
+  switchToLink(fromClick) {
+    if (this.linkMode && !fromClick) { return; }
+
+    clearTimeout(this.cancelBlur);
+    this.cancelBlur = null;
+    this.linkMode = true;
+    this.inputElement.hide();
+    this.linkElement.show();
+    this.updateValue(this.inputElement.val());
+  }
+
+  inputBlur() {
+    // happens long before the click event on the typeahead options
+    // need to have long delay because the blur
+    this.cancelBlur = setTimeout(this.switchToLink.bind(this), 200);
+  }
+
+  updateValue(text) {
+    if (text === '' || this.text === text) {
+      return;
+    }
+
+    this.$scope.$apply(() => {
+      var option = _.find(this.options, {text: text});
+
+      if (option) {
+        this.value = option.value;
+        this.updateDisplay(option.text);
+      } else if (this.allowCustom) {
+        this.value = text;
+        this.updateDisplay(text);
+      }
+
+      // needs to call this after digest so
+      // property is synced with outerscope
+      this.$scope.$$postDigest(() => {
+        this.$scope.$apply(() => {
+          this.onChange();
+        });
+      });
+
+    });
+  }
+
+  updateDisplay(text) {
+    this.text = text;
+    this.display = this.$sce.trustAsHtml(this.templateSrv.highlightVariablesAsHtml(text));
+  }
+
+  open() {
+    this.inputElement.show();
+
+    this.inputElement.css('width', (Math.max(this.linkElement.width(), 80) + 16) + 'px');
+    this.inputElement.focus();
+
+    this.linkElement.hide();
+    this.linkMode = false;
+
+    var typeahead = this.inputElement.data('typeahead');
+    if (typeahead) {
+      this.inputElement.val('');
+      typeahead.lookup();
+    }
+  }
+}
+
+
+
+export function formDropdownDirective() {
+  return {
+    restrict: 'E',
+    templateUrl: 'public/app/core/components/form_dropdown/form_dropdown.html',
+    controller: FormDropdownCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+    scope: {
+      value: "=",
+      options: "=",
+      getOptions: "&",
+      onChange: "&",
+      cssClass: "@",
+      allowCustom: "@",
+    },
+    link: function() {
+    }
+  };
+}
+
+coreModule.directive('gfFormDropdown', formDropdownDirective);

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

@@ -35,6 +35,7 @@ import {switchDirective} from './components/switch';
 import {dashboardSelector} from './components/dashboard_selector';
 import {queryPartEditorDirective} from './components/query_part/query_part_editor';
 import {WizardFlow} from './components/wizard/wizard';
+import {formDropdownDirective} from './components/form_dropdown/form_dropdown';
 import 'app/core/controllers/all';
 import 'app/core/services/all';
 import 'app/core/routes/routes';
@@ -62,4 +63,5 @@ export {
   queryPartEditorDirective,
   WizardFlow,
   colors,
+  formDropdownDirective,
 };

+ 0 - 2
public/app/core/directives/metric_segment.js

@@ -143,7 +143,6 @@ function (_, $, coreModule) {
           $input.focus();
 
           linkMode = false;
-
           var typeahead = $input.data('typeahead');
           if (typeahead) {
             $input.val('');
@@ -152,7 +151,6 @@ function (_, $, coreModule) {
         });
 
         $input.blur($scope.inputBlur);
-
         $compile(elem.contents())($scope);
       }
     };

+ 2 - 1
public/app/plugins/datasource/elasticsearch/partials/bucket_agg.html

@@ -5,7 +5,8 @@
 			<span ng-hide="isFirst">Then by</span>
 		</label>
 
-		<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="width-10"></metric-segment-model>
+		<gf-form-dropdown value="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="width-10"></gf-form-dropdown>
+		<!-- <metric&#45;segment&#45;model property="agg.type" options="bucketAggTypes" on&#45;change="onTypeChanged()" custom="false" css&#45;class="width&#45;10"></metric&#45;segment&#45;model> -->
 		<metric-segment-model ng-if="agg.field" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment-model>
 	</div>
 

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

@@ -11,9 +11,9 @@
 	</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>
+		<gf-form-dropdown value="agg.type" options="metricAggTypes" on-change="onTypeChanged()" custom="false" css-class="width-10"></gf-form-dropdown>
+		<gf-form-dropdown ng-if="aggDef.requiresField" value="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></gf-form-dropdown>
+		<gf-form-dropdown ng-if="aggDef.isPipelineAgg" value="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="width-12"></gf-form-dropdown>
 	</div>
 
 	<div class="gf-form gf-form--grow">