grafana/public/app/plugins/datasource/graphite/func_editor.ts
kay delaney bad048b7ba
Performance: Standardize lodash imports to use destructured members (#33040)
* Performance: Standardize lodash imports to use destructured members
Changes lodash imports of the form `import x from 'lodash/x'` to
`import { x } from 'lodash'` to reduce bundle size.

* Remove unnecessary _ import from Graph component

* Enforce lodash import style

* Fix remaining lodash imports
2021-04-21 09:38:00 +02:00

253 lines
7.1 KiB
TypeScript

import { assign, clone, each, last, map, partial } from 'lodash';
import $ from 'jquery';
import coreModule from 'app/core/core_module';
import { TemplateSrv } from 'app/features/templating/template_srv';
/** @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>';
return {
restrict: 'A',
link: function postLink($scope: any, elem: JQuery) {
const $funcLink = $(funcSpanTemplate);
const ctrl = $scope.ctrl;
const func = $scope.func;
let scheduledRelink = false;
let paramCountAtLink = 0;
let cancelBlur: any = null;
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) {
const $link = $(this);
const $comma = $link.prev('.comma');
const $input = $link.next();
$input.val(func.params[paramIndex]);
$comma.removeClass('query-part__last');
$link.hide();
$input.show();
$input.focus();
$input.select();
const typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(() => {
relink();
scheduledRelink = false;
}, 200);
}
}
function paramDef(index: number) {
if (index < func.def.params.length) {
return func.def.params[index];
}
if ((last(func.def.params) as any).multiple) {
return assign({}, last(func.def.params), { optional: true });
}
return {};
}
function switchToLink(inputElem: HTMLElement, paramIndex: any) {
const $input = $(inputElem);
clearTimeout(cancelBlur);
cancelBlur = null;
const $link = $input.prev();
const $comma = $link.prev('.comma');
const newValue = $input.val() as any;
// remove optional empty params
if (newValue !== '' || paramDef(paramIndex).optional) {
func.updateParam(newValue, paramIndex);
$link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : '&nbsp;');
}
scheduledRelinkIfNeeded();
$scope.$apply(() => {
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) {
const inputElem = this;
// happens long before the click event on the typeahead options
// need to have long delay because the blur
cancelBlur = setTimeout(() => {
switchToLink(inputElem, paramIndex);
}, 200);
}
function inputKeyPress(this: any, paramIndex: any, e: any) {
if (e.which === 13) {
$(this).blur();
}
}
function inputKeyDown(this: any) {
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input: any, paramIndex: any) {
$input.attr('data-provide', 'typeahead');
let options = paramDef(paramIndex).options;
if (paramDef(paramIndex).type === 'int') {
options = map(options, (val) => {
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: (value: any) => {
$input.val(value);
switchToLink($input[0], paramIndex);
return value;
},
});
const typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function addElementsAndCompile() {
$funcLink.appendTo(elem);
if (func.def.unknown) {
elem.addClass('unknown-function');
}
const defParams: any = clone(func.def.params);
const lastParam: any = last(func.def.params);
while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
defParams.push(assign({}, lastParam, { optional: true }));
}
each(defParams, (param: any, index: number) => {
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';
}
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';
}
if (index > 0) {
$('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem);
}
const $paramLink = $(`<a ng-click="" class="${linkClass}">${paramValue}</a>`);
const $input = $(paramTemplate);
$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(() => {
elem.find('.query-part__link').first().click();
}, 10);
}
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFirstParam();
}
relink();
},
};
}
coreModule.directive('graphiteFuncEditor', graphiteFuncEditor);