fix function editor in Grafana 6, #765
This commit is contained in:
109
src/datasource-zabbix/FunctionEditor.tsx
Normal file
109
src/datasource-zabbix/FunctionEditor.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
// import rst2html from 'rst2html';
|
||||
import { FunctionDescriptor, FunctionEditorControlsProps, FunctionEditorControls } from './FunctionEditorControls';
|
||||
|
||||
// @ts-ignore
|
||||
import { PopperController, Popper } from '@grafana/ui';
|
||||
|
||||
interface FunctionEditorProps extends FunctionEditorControlsProps {
|
||||
func: FunctionDescriptor;
|
||||
}
|
||||
|
||||
interface FunctionEditorState {
|
||||
showingDescription: boolean;
|
||||
}
|
||||
|
||||
class FunctionEditor extends React.PureComponent<FunctionEditorProps, FunctionEditorState> {
|
||||
private triggerRef = React.createRef<HTMLSpanElement>();
|
||||
|
||||
constructor(props: FunctionEditorProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showingDescription: false,
|
||||
};
|
||||
}
|
||||
|
||||
renderContent = ({ updatePopperPosition }) => {
|
||||
const {
|
||||
onMoveLeft,
|
||||
onMoveRight,
|
||||
func: {
|
||||
def: { name, description },
|
||||
},
|
||||
} = this.props;
|
||||
const { showingDescription } = this.state;
|
||||
|
||||
if (showingDescription) {
|
||||
return (
|
||||
<div style={{ overflow: 'auto', maxHeight: '30rem', textAlign: 'left', fontWeight: 'normal' }}>
|
||||
<h4 style={{ color: 'white' }}> {name} </h4>
|
||||
<div>{description}</div>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FunctionEditorControls
|
||||
{...this.props}
|
||||
onMoveLeft={() => {
|
||||
onMoveLeft(this.props.func);
|
||||
updatePopperPosition();
|
||||
}}
|
||||
onMoveRight={() => {
|
||||
onMoveRight(this.props.func);
|
||||
updatePopperPosition();
|
||||
}}
|
||||
onDescriptionShow={() => {
|
||||
this.setState({ showingDescription: true }, () => {
|
||||
updatePopperPosition();
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<PopperController content={this.renderContent} placement="top" hideAfter={300}>
|
||||
{(showPopper, hidePopper, popperProps) => {
|
||||
return (
|
||||
<>
|
||||
{this.triggerRef && (
|
||||
<Popper
|
||||
{...popperProps}
|
||||
referenceElement={this.triggerRef.current}
|
||||
wrapperClassName="popper"
|
||||
className="popper__background"
|
||||
onMouseLeave={() => {
|
||||
this.setState({ showingDescription: false });
|
||||
hidePopper();
|
||||
}}
|
||||
onMouseEnter={showPopper}
|
||||
renderArrow={({ arrowProps, placement }) => (
|
||||
<div className="popper__arrow" data-placement={placement} {...arrowProps} />
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<span
|
||||
ref={this.triggerRef}
|
||||
onClick={popperProps.show ? hidePopper : showPopper}
|
||||
onMouseLeave={() => {
|
||||
hidePopper();
|
||||
this.setState({ showingDescription: false });
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{this.props.func.def.name}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</PopperController>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { FunctionEditor };
|
||||
67
src/datasource-zabbix/FunctionEditorControls.tsx
Normal file
67
src/datasource-zabbix/FunctionEditorControls.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
|
||||
const DOCS_FUNC_REF_URL = 'https://alexanderzobnin.github.io/grafana-zabbix/reference/functions/';
|
||||
|
||||
export interface FunctionDescriptor {
|
||||
text: string;
|
||||
params: string[];
|
||||
def: {
|
||||
category: string;
|
||||
defaultParams: string[];
|
||||
description?: string;
|
||||
fake: boolean;
|
||||
name: string;
|
||||
params: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface FunctionEditorControlsProps {
|
||||
onMoveLeft: (func: FunctionDescriptor) => void;
|
||||
onMoveRight: (func: FunctionDescriptor) => void;
|
||||
onRemove: (func: FunctionDescriptor) => void;
|
||||
}
|
||||
|
||||
const FunctionHelpButton = (props: { description: string; name: string; onDescriptionShow: () => void }) => {
|
||||
if (props.description) {
|
||||
return <span className="pointer fa fa-question-circle" onClick={props.onDescriptionShow} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className="pointer fa fa-question-circle"
|
||||
onClick={() => {
|
||||
window.open(
|
||||
DOCS_FUNC_REF_URL + '#' + props.name,
|
||||
'_blank'
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const FunctionEditorControls = (
|
||||
props: FunctionEditorControlsProps & {
|
||||
func: FunctionDescriptor;
|
||||
onDescriptionShow: () => void;
|
||||
}
|
||||
) => {
|
||||
const { func, onMoveLeft, onMoveRight, onRemove, onDescriptionShow } = props;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: '60px',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<span className="pointer fa fa-arrow-left" onClick={() => onMoveLeft(func)} />
|
||||
<FunctionHelpButton
|
||||
name={func.def.name}
|
||||
description={func.def.description}
|
||||
onDescriptionShow={onDescriptionShow}
|
||||
/>
|
||||
<span className="pointer fa fa-remove" onClick={() => onRemove(func)} />
|
||||
<span className="pointer fa fa-arrow-right" onClick={() => onMoveRight(func)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,248 +0,0 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
|
||||
const DOCS_FUNC_REF_URL = 'https://alexanderzobnin.github.io/grafana-zabbix/reference/functions/';
|
||||
|
||||
angular
|
||||
.module('grafana.directives')
|
||||
.directive('metricFunctionEditor',
|
||||
|
||||
/** @ngInject */
|
||||
function($compile, templateSrv) {
|
||||
|
||||
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
|
||||
var paramTemplate = '<input type="text" style="display:none"' +
|
||||
' class="input-mini tight-form-func-param"></input>';
|
||||
|
||||
var funcControlsTemplate =
|
||||
'<div class="tight-form-func-controls">' +
|
||||
'<span class="pointer fa fa-arrow-left"></span>' +
|
||||
'<span class="pointer fa fa-question-circle"></span>' +
|
||||
'<span class="pointer fa fa-remove" ></span>' +
|
||||
'<span class="pointer fa fa-arrow-right"></span>' +
|
||||
'</div>';
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function postLink($scope, elem) {
|
||||
var $funcLink = $(funcSpanTemplate);
|
||||
var $funcControls = $(funcControlsTemplate);
|
||||
var ctrl = $scope.ctrl;
|
||||
var func = $scope.func;
|
||||
var funcDef = func.def;
|
||||
var scheduledRelink = false;
|
||||
var paramCountAtLink = 0;
|
||||
|
||||
function clickFuncParam(paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
|
||||
var $link = $(this);
|
||||
var $input = $link.next();
|
||||
|
||||
$input.val(func.params[paramIndex]);
|
||||
$input.css('width', ($link.width() + 16) + 'px');
|
||||
|
||||
$link.hide();
|
||||
$input.show();
|
||||
$input.focus();
|
||||
$input.select();
|
||||
|
||||
var typeahead = $input.data('typeahead');
|
||||
if (typeahead) {
|
||||
$input.val('');
|
||||
typeahead.lookup();
|
||||
}
|
||||
}
|
||||
|
||||
function scheduledRelinkIfNeeded() {
|
||||
if (paramCountAtLink === func.params.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scheduledRelink) {
|
||||
scheduledRelink = true;
|
||||
setTimeout(function() {
|
||||
relink();
|
||||
scheduledRelink = false;
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
function inputBlur(paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
var $input = $(this);
|
||||
var $link = $input.prev();
|
||||
var newValue = $input.val();
|
||||
|
||||
if (newValue !== '' || func.def.params[paramIndex].optional) {
|
||||
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
|
||||
|
||||
func.updateParam($input.val(), paramIndex);
|
||||
scheduledRelinkIfNeeded();
|
||||
|
||||
$scope.$apply(function() {
|
||||
ctrl.targetChanged();
|
||||
});
|
||||
|
||||
$input.hide();
|
||||
$link.show();
|
||||
}
|
||||
}
|
||||
|
||||
function inputKeyPress(paramIndex, e) {
|
||||
/*jshint validthis:true */
|
||||
if(e.which === 13) {
|
||||
inputBlur.call(this, paramIndex);
|
||||
}
|
||||
}
|
||||
|
||||
function inputKeyDown() {
|
||||
/*jshint validthis:true */
|
||||
this.style.width = (3 + this.value.length) * 8 + 'px';
|
||||
}
|
||||
|
||||
function addTypeahead($input, paramIndex) {
|
||||
$input.attr('data-provide', 'typeahead');
|
||||
|
||||
var options = funcDef.params[paramIndex].options;
|
||||
if (funcDef.params[paramIndex].type === 'int' ||
|
||||
funcDef.params[paramIndex].type === 'float') {
|
||||
options = _.map(options, function(val) { return val.toString(); });
|
||||
}
|
||||
|
||||
$input.typeahead({
|
||||
source: options,
|
||||
minLength: 0,
|
||||
items: 20,
|
||||
updater: function (value) {
|
||||
setTimeout(function() {
|
||||
inputBlur.call($input[0], paramIndex);
|
||||
}, 0);
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
var typeahead = $input.data('typeahead');
|
||||
typeahead.lookup = function () {
|
||||
this.query = this.$element.val() || '';
|
||||
return this.process(this.source);
|
||||
};
|
||||
}
|
||||
|
||||
function toggleFuncControls() {
|
||||
var targetDiv = elem.closest('.tight-form');
|
||||
|
||||
if (elem.hasClass('show-function-controls')) {
|
||||
elem.removeClass('show-function-controls');
|
||||
targetDiv.removeClass('has-open-function');
|
||||
$funcControls.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
elem.addClass('show-function-controls');
|
||||
targetDiv.addClass('has-open-function');
|
||||
|
||||
$funcControls.show();
|
||||
}
|
||||
|
||||
function addElementsAndCompile() {
|
||||
$funcControls.appendTo(elem);
|
||||
$funcLink.appendTo(elem);
|
||||
|
||||
_.each(funcDef.params, function(param, index) {
|
||||
if (param.optional && func.params.length <= index) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
$('<span>, </span>').appendTo(elem);
|
||||
}
|
||||
|
||||
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
|
||||
var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + paramValue + '</a>');
|
||||
var $input = $(paramTemplate);
|
||||
|
||||
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 (funcDef.params[index].options) {
|
||||
addTypeahead($input, index);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('<span>)</span>').appendTo(elem);
|
||||
|
||||
$compile(elem.contents())($scope);
|
||||
}
|
||||
|
||||
function ifJustAddedFocusFistParam() {
|
||||
if ($scope.func.added) {
|
||||
$scope.func.added = false;
|
||||
setTimeout(function() {
|
||||
elem.find('.graphite-func-param-link').first().click();
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
function registerFuncControlsToggle() {
|
||||
$funcLink.click(toggleFuncControls);
|
||||
}
|
||||
|
||||
function registerFuncControlsActions() {
|
||||
$funcControls.click(function(e) {
|
||||
var $target = $(e.target);
|
||||
if ($target.hasClass('fa-remove')) {
|
||||
toggleFuncControls();
|
||||
$scope.$apply(function() {
|
||||
ctrl.removeFunction($scope.func);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ($target.hasClass('fa-arrow-left')) {
|
||||
$scope.$apply(function() {
|
||||
_.move($scope.target.functions, $scope.$index, $scope.$index - 1);
|
||||
ctrl.targetChanged();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ($target.hasClass('fa-arrow-right')) {
|
||||
$scope.$apply(function() {
|
||||
_.move($scope.target.functions, $scope.$index, $scope.$index + 1);
|
||||
ctrl.targetChanged();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ($target.hasClass('fa-question-circle')) {
|
||||
var docSite = DOCS_FUNC_REF_URL;
|
||||
window.open(docSite + '#' + funcDef.name.toLowerCase(),'_blank');
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function relink() {
|
||||
elem.children().remove();
|
||||
|
||||
addElementsAndCompile();
|
||||
ifJustAddedFocusFistParam();
|
||||
registerFuncControlsToggle();
|
||||
registerFuncControlsActions();
|
||||
}
|
||||
|
||||
relink();
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
256
src/datasource-zabbix/metric-function-editor.directive.ts
Normal file
256
src/datasource-zabbix/metric-function-editor.directive.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import coreModule from 'grafana/app/core/core_module';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import { react2AngularDirective } from './react2angular';
|
||||
import { FunctionEditor } from './FunctionEditor';
|
||||
|
||||
/** @ngInject */
|
||||
export function zabbixFunctionEditor($compile, templateSrv) {
|
||||
const funcSpanTemplate = `
|
||||
<zbx-function-editor
|
||||
func="func"
|
||||
onRemove="ctrl.handleRemoveFunction"
|
||||
onMoveLeft="ctrl.handleMoveLeft"
|
||||
onMoveRight="ctrl.handleMoveRight"
|
||||
/><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, elem) {
|
||||
const $funcLink = $(funcSpanTemplate);
|
||||
const ctrl = $scope.ctrl;
|
||||
const func = $scope.func;
|
||||
let scheduledRelink = false;
|
||||
let paramCountAtLink = 0;
|
||||
let cancelBlur = null;
|
||||
|
||||
ctrl.handleRemoveFunction = func => {
|
||||
ctrl.removeFunction(func);
|
||||
};
|
||||
|
||||
ctrl.handleMoveLeft = func => {
|
||||
ctrl.moveFunction(func, -1);
|
||||
};
|
||||
|
||||
ctrl.handleMoveRight = func => {
|
||||
ctrl.moveFunction(func, 1);
|
||||
};
|
||||
|
||||
function clickFuncParam(this: any, paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
|
||||
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) {
|
||||
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, paramIndex) {
|
||||
/*jshint validthis:true */
|
||||
const $input = $(inputElem);
|
||||
|
||||
clearTimeout(cancelBlur);
|
||||
cancelBlur = null;
|
||||
|
||||
const $link = $input.prev();
|
||||
const $comma = $link.prev('.comma');
|
||||
const newValue = $input.val();
|
||||
|
||||
// remove optional empty params
|
||||
if (newValue !== '' || paramDef(paramIndex).optional) {
|
||||
func.updateParam(newValue, paramIndex);
|
||||
$link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : ' ');
|
||||
}
|
||||
|
||||
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) {
|
||||
/*jshint validthis:true */
|
||||
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, e) {
|
||||
/*jshint validthis:true */
|
||||
if (e.which === 13) {
|
||||
$(this).blur();
|
||||
}
|
||||
}
|
||||
|
||||
function inputKeyDown(this: any) {
|
||||
/*jshint validthis:true */
|
||||
this.style.width = (3 + this.value.length) * 8 + 'px';
|
||||
}
|
||||
|
||||
function addTypeahead($input, paramIndex) {
|
||||
$input.attr('data-provide', 'typeahead');
|
||||
|
||||
let options = paramDef(paramIndex).options;
|
||||
if (paramDef(paramIndex).type === 'int' || paramDef(paramIndex).type === 'float') {
|
||||
options = _.map(options, val => {
|
||||
return val.toString();
|
||||
});
|
||||
}
|
||||
|
||||
$input.typeahead({
|
||||
source: options,
|
||||
minLength: 0,
|
||||
items: 20,
|
||||
updater: value => {
|
||||
$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);
|
||||
|
||||
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;
|
||||
|
||||
const last = index >= func.params.length - 1 && param.optional && !hasValue;
|
||||
if (last && param.multiple) {
|
||||
paramValue = '+';
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
$('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem);
|
||||
}
|
||||
|
||||
const $paramLink = $(
|
||||
'<a ng-click="" class="graphite-func-param-link' +
|
||||
(last ? ' query-part__last' : '') +
|
||||
'">' +
|
||||
(hasValue ? 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('.graphite-func-param-link')
|
||||
.first()
|
||||
.click();
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
function relink() {
|
||||
elem.children().remove();
|
||||
addElementsAndCompile();
|
||||
ifJustAddedFocusFirstParam();
|
||||
}
|
||||
|
||||
relink();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('zabbixFunctionEditor', zabbixFunctionEditor);
|
||||
|
||||
react2AngularDirective('zbxFunctionEditor', FunctionEditor, ['func', 'onRemove', 'onMoveLeft', 'onMoveRight']);
|
||||
@@ -204,7 +204,9 @@
|
||||
<div class="gf-form-inline" ng-show="ctrl.target.mode == editorMode.METRICS || ctrl.target.mode == editorMode.ITEMID">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">Functions</label>
|
||||
<div ng-repeat="func in ctrl.target.functions" class="gf-form-label query-part" metric-function-editor></div>
|
||||
</div>
|
||||
<div ng-repeat="func in ctrl.target.functions" class="gf-form">
|
||||
<span zabbix-function-editor class="gf-form-label query-part" ng-hide="func.hidden"></span>
|
||||
</div>
|
||||
<div class="gf-form dropdown" add-metric-function>
|
||||
</div>
|
||||
|
||||
@@ -275,6 +275,12 @@ export class ZabbixQueryController extends QueryCtrl {
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
moveFunction(func, offset) {
|
||||
const index = this.target.functions.indexOf(func);
|
||||
_.move(this.target.functions, index, index + offset);
|
||||
this.targetChanged();
|
||||
}
|
||||
|
||||
moveAliasFuncLast() {
|
||||
var aliasFunc = _.find(this.target.functions, function(func) {
|
||||
return func.def.name === 'alias' ||
|
||||
|
||||
10
src/datasource-zabbix/react2angular.ts
Normal file
10
src/datasource-zabbix/react2angular.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import coreModule from 'grafana/app/core/core_module';
|
||||
|
||||
export function react2AngularDirective(name: string, component: any, options: any) {
|
||||
coreModule.directive(name, [
|
||||
'reactDirective',
|
||||
reactDirective => {
|
||||
return reactDirective(component, options);
|
||||
},
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user