poc for new metric segment

This commit is contained in:
Torkel Ödegaard
2016-10-08 10:06:47 +02:00
parent 913f17ee8a
commit b65642564a
6 changed files with 202 additions and 6 deletions
@@ -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>
@@ -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
View File
@@ -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,
};
@@ -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);
}
};
@@ -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>
@@ -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">