grafana/public/app/plugins/datasource/graphite/func_editor.ts

252 lines
7.1 KiB
TypeScript
Raw Normal View History

2018-03-20 06:36:02 -05:00
import _ from 'lodash';
import $ from 'jquery';
import coreModule from 'app/core/core_module';
import { TemplateSrv } from 'app/features/templating/template_srv';
2018-03-20 06:36:02 -05:00
2018-04-03 12:39:50 -05:00
/** @ngInject */
export function graphiteFuncEditor($compile: any, templateSrv: TemplateSrv) {
const funcSpanTemplate = `
<function-editor
func="func"
onRemove="ctrl.handleRemoveFunction"
onMoveLeft="ctrl.handleMoveLeft"
onMoveRight="ctrl.handleMoveRight">
</function-editor>
<span>(</span>
`;
const paramTemplate =
'<input type="text" style="display:none"' + ' class="input-small tight-form-func-param"></input>';
2018-03-20 06:36:02 -05:00
return {
restrict: 'A',
link: function postLink($scope: any, elem: JQuery) {
2018-08-29 07:26:50 -05:00
const $funcLink = $(funcSpanTemplate);
const ctrl = $scope.ctrl;
const func = $scope.func;
let scheduledRelink = false;
let paramCountAtLink = 0;
let cancelBlur: any = null;
2018-03-20 06:36:02 -05:00
ctrl.handleRemoveFunction = (func: any) => {
ctrl.removeFunction(func);
};
ctrl.handleMoveLeft = (func: any) => {
ctrl.moveFunction(func, -1);
};
ctrl.handleMoveRight = (func: any) => {
ctrl.moveFunction(func, 1);
};
function clickFuncParam(this: any, paramIndex: any) {
2018-08-29 07:26:50 -05:00
const $link = $(this);
const $comma = $link.prev('.comma');
const $input = $link.next();
2018-03-20 06:36:02 -05:00
$input.val(func.params[paramIndex]);
$comma.removeClass('query-part__last');
$link.hide();
$input.show();
$input.focus();
$input.select();
2018-08-29 07:26:50 -05:00
const typeahead = $input.data('typeahead');
2018-03-20 06:36:02 -05:00
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(() => {
2018-03-20 06:36:02 -05:00
relink();
scheduledRelink = false;
}, 200);
}
}
function paramDef(index: number) {
2018-03-20 06:36:02 -05:00
if (index < func.def.params.length) {
return func.def.params[index];
}
2019-04-15 05:11:52 -05:00
if ((_.last(func.def.params) as any).multiple) {
2018-03-20 06:36:02 -05:00
return _.assign({}, _.last(func.def.params), { optional: true });
}
return {};
}
function switchToLink(inputElem: HTMLElement, paramIndex: any) {
2018-08-29 07:26:50 -05:00
const $input = $(inputElem);
2018-03-20 06:36:02 -05:00
clearTimeout(cancelBlur);
cancelBlur = null;
2018-08-29 07:26:50 -05:00
const $link = $input.prev();
const $comma = $link.prev('.comma');
const newValue = $input.val();
2018-03-20 06:36:02 -05:00
// remove optional empty params
if (newValue !== '' || paramDef(paramIndex).optional) {
func.updateParam(newValue, paramIndex);
$link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : '&nbsp;');
}
scheduledRelinkIfNeeded();
$scope.$apply(() => {
2018-03-20 06:36:02 -05:00
ctrl.targetChanged();
});
if ($link.hasClass('query-part__last') && newValue === '') {
$comma.addClass('query-part__last');
} else {
$link.removeClass('query-part__last');
}
$input.hide();
$link.show();
}
// this = input element
function inputBlur(this: any, paramIndex: any) {
2018-08-29 07:26:50 -05:00
const inputElem = this;
2018-03-20 06:36:02 -05:00
// happens long before the click event on the typeahead options
// need to have long delay because the blur
cancelBlur = setTimeout(() => {
2018-03-20 06:36:02 -05:00
switchToLink(inputElem, paramIndex);
}, 200);
}
function inputKeyPress(this: any, paramIndex: any, e: any) {
2018-03-20 06:36:02 -05:00
if (e.which === 13) {
$(this).blur();
}
}
function inputKeyDown(this: any) {
2018-03-20 06:36:02 -05:00
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input: any, paramIndex: any) {
2018-03-20 06:36:02 -05:00
$input.attr('data-provide', 'typeahead');
let options = paramDef(paramIndex).options;
2018-03-20 06:36:02 -05:00
if (paramDef(paramIndex).type === 'int') {
options = _.map(options, val => {
2018-03-20 06:36:02 -05:00
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: (value: any) => {
2018-03-20 06:36:02 -05:00
$input.val(value);
switchToLink($input[0], paramIndex);
return value;
},
});
2018-08-29 07:26:50 -05:00
const typeahead = $input.data('typeahead');
2018-03-20 06:36:02 -05:00
typeahead.lookup = function() {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function addElementsAndCompile() {
$funcLink.appendTo(elem);
2019-04-15 05:11:52 -05:00
const defParams: any = _.clone(func.def.params);
const lastParam: any = _.last(func.def.params);
2018-03-20 06:36:02 -05:00
while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
defParams.push(_.assign({}, lastParam, { optional: true }));
}
2019-04-15 05:11:52 -05:00
_.each(defParams, (param: any, index: number) => {
2018-03-20 06:36:02 -05:00
if (param.optional && func.params.length < index) {
return false;
}
let paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
const hasValue = paramValue !== null && paramValue !== undefined && paramValue !== '';
const last = index >= func.params.length - 1 && param.optional && !hasValue;
let linkClass = 'query-part__link';
if (last) {
linkClass += ' query-part__last';
}
2018-03-20 06:36:02 -05:00
if (last && param.multiple) {
paramValue = '+';
} else if (!hasValue) {
// for params with no value default to param name
paramValue = param.name;
linkClass += ' query-part__link--no-value';
2018-03-20 06:36:02 -05:00
}
if (index > 0) {
$('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem);
}
const $paramLink = $(`<a ng-click="" class="${linkClass}">${paramValue}</a>`);
2018-08-29 07:26:50 -05:00
const $input = $(paramTemplate);
2018-03-20 06:36:02 -05:00
$input.attr('placeholder', param.name);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
if (param.options) {
addTypeahead($input, index);
}
return true;
});
$('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFirstParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(() => {
2018-03-20 06:36:02 -05:00
elem
.find('.query-part__link')
2018-03-20 06:36:02 -05:00
.first()
.click();
}, 10);
}
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFirstParam();
}
relink();
},
};
}
coreModule.directive('graphiteFuncEditor', graphiteFuncEditor);