add sql_part component

pull/11081/head
Sven Klemm 8 years ago
parent b12d049f6f
commit 181dfdba04
  1. 118
      public/app/core/components/sql_part/sql_part.ts
  2. 185
      public/app/core/components/sql_part/sql_part_editor.ts
  3. 2
      public/app/core/core.ts

@ -0,0 +1,118 @@
import _ from 'lodash';
export class SqlPartDef {
type: string;
params: any[];
defaultParams: any[];
renderer: any;
category: any;
addStrategy: any;
constructor(options: any) {
this.type = options.type;
this.params = options.params;
this.defaultParams = options.defaultParams;
this.renderer = options.renderer;
this.category = options.category;
this.addStrategy = options.addStrategy;
}
}
export class SqlPart {
part: any;
def: SqlPartDef;
params: any[];
text: string;
constructor(part: any, def: any) {
this.part = part;
this.def = def;
if (!this.def) {
throw { message: 'Could not find query part ' + part.type };
}
part.params = part.params || _.clone(this.def.defaultParams);
this.params = part.params;
this.updateText();
}
render(innerExpr: string) {
return this.def.renderer(this, innerExpr);
}
hasMultipleParamsInString(strValue, index) {
if (strValue.indexOf(',') === -1) {
return false;
}
return this.def.params[index + 1] && this.def.params[index + 1].optional;
}
updateParam(strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (this.hasMultipleParamsInString(strValue, index)) {
_.each(strValue.split(','), (partVal, idx) => {
this.updateParam(partVal.trim(), idx);
});
return;
}
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.part.params = this.params;
this.updateText();
}
updateText() {
if (this.params.length === 0) {
this.text = this.def.type + '()';
return;
}
var text = this.def.type + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
}
}
export function functionRenderer(part, innerExpr) {
var str = part.def.type + '(';
var parameters = _.map(part.params, (value, index) => {
var paramType = part.def.params[index];
if (paramType.type === 'time') {
if (value === 'auto') {
value = '$__interval';
}
}
if (paramType.quote === 'single') {
return "'" + value + "'";
} else if (paramType.quote === 'double') {
return '"' + value + '"';
}
return value;
});
if (innerExpr) {
parameters.unshift(innerExpr);
}
return str + parameters.join(', ') + ')';
}
export function suffixRenderer(part, innerExpr) {
return innerExpr + ' ' + part.params[0];
}
export function identityRenderer(part, innerExpr) {
return part.params[0];
}
export function quotedIdentityRenderer(part, innerExpr) {
return '"' + part.params[0] + '"';
}

@ -0,0 +1,185 @@
import _ from 'lodash';
import $ from 'jquery';
import coreModule from 'app/core/core_module';
var template = `
<div class="dropdown cascade-open">
<a ng-click="showActionsMenu()" class="query-part-name pointer dropdown-toggle" data-toggle="dropdown">{{part.def.type}}</a>
<span>(</span><span class="query-part-parameters"></span><span>)</span>
<ul class="dropdown-menu">
<li ng-repeat="action in partActions">
<a ng-click="triggerPartAction(action)">{{action.text}}</a>
</li>
</ul>
`;
/** @ngInject */
export function sqlPartEditorDirective($compile, templateSrv) {
var paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>';
return {
restrict: 'E',
template: template,
scope: {
part: '=',
handleEvent: '&',
debounce: '@',
},
link: function postLink($scope, elem) {
var part = $scope.part;
var partDef = part.def;
var $paramsContainer = elem.find('.query-part-parameters');
var debounceLookup = $scope.debounce;
$scope.partActions = [];
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(part.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 inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || part.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
part.updateParam($input.val(), paramIndex);
$scope.$apply(() => {
$scope.handleEvent({ $event: { name: 'part-param-changed' } });
});
}
$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, param, paramIndex) {
if (!param.options && !param.dynamicLookup) {
return;
}
var typeaheadSource = function(query, callback) {
if (param.options) {
var options = param.options;
if (param.type === 'int') {
options = _.map(options, function(val) {
return val.toString();
});
}
return options;
}
$scope.$apply(function() {
$scope.handleEvent({ $event: { name: 'get-param-options' } }).then(function(result) {
var dynamicOptions = _.map(result, function(op) {
return op.value;
});
callback(dynamicOptions);
});
});
};
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: typeaheadSource,
minLength: 0,
items: 1000,
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() || '';
var items = this.source(this.query, $.proxy(this.process, this));
return items ? this.process(items) : items;
};
if (debounceLookup) {
typeahead.lookup = _.debounce(typeahead.lookup, 500, { leading: true });
}
}
$scope.showActionsMenu = function() {
$scope.handleEvent({ $event: { name: 'get-part-actions' } }).then(res => {
$scope.partActions = res;
});
};
$scope.triggerPartAction = function(action) {
$scope.handleEvent({ $event: { name: 'action', action: action } });
};
function addElementsAndCompile() {
_.each(partDef.params, function(param, index) {
if (param.optional && part.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo($paramsContainer);
}
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
var $input = $(paramTemplate);
$paramLink.appendTo($paramsContainer);
$input.appendTo($paramsContainer);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
addTypeahead($input, param, index);
});
}
function relink() {
$paramsContainer.empty();
addElementsAndCompile();
}
relink();
},
};
}
coreModule.directive('sqlPartEditor', sqlPartEditorDirective);

@ -31,6 +31,7 @@ import { layoutSelector } from './components/layout_selector/layout_selector';
import { switchDirective } from './components/switch';
import { dashboardSelector } from './components/dashboard_selector';
import { queryPartEditorDirective } from './components/query_part/query_part_editor';
import { sqlPartEditorDirective } from './components/sql_part/sql_part_editor';
import { formDropdownDirective } from './components/form_dropdown/form_dropdown';
import 'app/core/controllers/all';
import 'app/core/services/all';
@ -74,6 +75,7 @@ export {
appEvents,
dashboardSelector,
queryPartEditorDirective,
sqlPartEditorDirective,
colors,
formDropdownDirective,
assignModelProperties,

Loading…
Cancel
Save