1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

autocomplete improvements...

* add minInput - fixes #3479
* Add missing entries for catch, status, tcp, udp, websocket
* corrections for http request
* match visibility improvements (mono font + match color)
* match to variable source as well as variable name
This commit is contained in:
Steve-Mcl 2022-04-05 14:00:14 +01:00
parent cb88409102
commit 475113838a
2 changed files with 104 additions and 60 deletions

View File

@ -15,6 +15,8 @@
* The function must either return auto-complete options, or pass them * The function must either return auto-complete options, or pass them
* to the optional 'done' parameter. * to the optional 'done' parameter.
* If the function signature includes 'done', it must be used * If the function signature includes 'done', it must be used
* minLength: number
* If `minLength` is 0, pressing down arrow will show the list
* *
* The auto-complete options should be an array of objects in the form: * The auto-complete options should be an array of objects in the form:
* { * {
@ -26,10 +28,11 @@
$.widget( "nodered.autoComplete", { $.widget( "nodered.autoComplete", {
_create: function() { _create: function() {
var that = this; const that = this;
this.completionMenuShown = false; this.completionMenuShown = false;
this.options.search = this.options.search || function() { return [] } this.options.minLength = parseInteger(this.options.minLength, 1, 0);
this.element.addClass("red-ui-autoComplete") this.options.search = this.options.search || function() { return [] };
this.element.addClass("red-ui-autoComplete");
this.element.on("keydown.red-ui-autoComplete", function(evt) { this.element.on("keydown.red-ui-autoComplete", function(evt) {
if ((evt.keyCode === 13 || evt.keyCode === 9) && that.completionMenuShown) { if ((evt.keyCode === 13 || evt.keyCode === 9) && that.completionMenuShown) {
var opts = that.menu.options(); var opts = that.menu.options();
@ -71,8 +74,8 @@
this.completionMenuShown = true; this.completionMenuShown = true;
}, },
_updateCompletions: function(val) { _updateCompletions: function(val) {
var that = this; const that = this;
if (val.trim() === "") { if (val.trim().length < this.options.minLength) {
if (this.completionMenuShown) { if (this.completionMenuShown) {
this.menu.hide(); this.menu.hide();
} }
@ -96,7 +99,7 @@
} }
} }
if (this.options.search.length === 2) { if (this.options.search.length === 2) {
var requestId = 1+Math.floor(Math.random()*10000); const requestId = 1+Math.floor(Math.random()*10000);
this.pendingRequest = requestId; this.pendingRequest = requestId;
this.options.search(val,function(completions) { displayResults(completions,requestId);}) this.options.search(val,function(completions) { displayResults(completions,requestId);})
} else { } else {
@ -112,4 +115,12 @@
} }
} }
}); });
function parseInteger(input, def, min, max) {
if(input == null) { return (def || 0); }
min = min == null ? Number.NEGATIVE_INFINITY : min;
max = max == null ? Number.POSITIVE_INFINITY : max;
let n = parseInt(input);
if(isNaN(n) || n < min || n > max) { n = def || 0; }
return n;
}
})(jQuery); })(jQuery);

View File

@ -55,34 +55,46 @@
} }
var autoComplete = function(options) { var autoComplete = function(options) {
function getMatch(value, searchValue) {
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
const len = idx > -1 ? searchValue.length : 0;
return {
index: idx,
found: idx > -1,
pre: value.substring(0,idx),
match: value.substring(idx,idx+len),
post: value.substring(idx+len),
}
}
function generateSpans(match) {
const els = [];
if(match.pre) { els.push($('<span/>').text(match.pre)); }
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
if(match.post) { els.push($('<span/>').text(match.post)); }
return els;
}
return function(val) { return function(val) {
var matches = []; var matches = [];
options.forEach(opt => { options.forEach(opt => {
let v = opt.value; const optVal = opt.value;
var i = v.toLowerCase().indexOf(val.toLowerCase()); const optSrc = (opt.source||[]).join(",");
if (i > -1) { const valMatch = getMatch(optVal, val);
var pre = v.substring(0,i); const srcMatch = getMatch(optSrc, val);
var matchedVal = v.substring(i,i+val.length); if (valMatch.found || srcMatch.found) {
var post = v.substring(i+val.length) const element = $('<div>',{style: "display: flex"});
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
var el = $('<div/>',{style:"white-space:nowrap; overflow: hidden; flex-grow:1"}); valEl.append(generateSpans(valMatch));
$('<span/>').text(pre).appendTo(el); valEl.appendTo(element);
$('<span/>',{style:"font-weight: bold"}).text(matchedVal).appendTo(el); if (optSrc) {
$('<span/>').text(post).appendTo(el); const optEl = $('<div>').css({ "font-size": "0.8em" });
optEl.append(generateSpans(srcMatch));
var element = $('<div>',{style: "display: flex"}); optEl.appendTo(element);
el.appendTo(element);
if (opt.source) {
$('<div>').css({
"font-size": "0.8em"
}).text(opt.source.join(",")).appendTo(element);
} }
matches.push({ matches.push({
value: v, value: optVal,
label: element, label: element,
i:i i: (valMatch.found ? valMatch.index : srcMatch.index)
}) });
} }
}) })
matches.sort(function(A,B){return A.i-B.i}) matches.sort(function(A,B){return A.i-B.i})
@ -93,6 +105,36 @@
// This is a hand-generated list of completions for the core nodes (based on the node help html). // This is a hand-generated list of completions for the core nodes (based on the node help html).
var msgCompletions = [ var msgCompletions = [
{ value: "payload" }, { value: "payload" },
{ value: "topic", source: ["mqtt","inject","rbe"] },
{ value: "action", source: ["mqtt"] },
{ value: "complete", source: ["join"] },
{ value: "contentType", source: ["mqtt"] },
{ value: "cookies", source: ["http request","http response"] },
{ value: "correlationData", source: ["mqtt"] },
{ value: "delay", source: ["delay","trigger"] },
{ value: "encoding", source: ["file"] },
{ value: "error", source: ["catch"] },
{ value: "error.message", source: ["catch"] },
{ value: "error.source", source: ["catch"] },
{ value: "error.source.id", source: ["catch"] },
{ value: "error.source.type", source: ["catch"] },
{ value: "error.source.name", source: ["catch"] },
{ value: "filename", source: ["file","file in"] },
{ value: "flush", source: ["delay"] },
{ value: "followRedirects", source: ["http request"] },
{ value: "headers", source: ["http response","http request"] },
{ value: "host", source: ["tcp request","http request"] },
{ value: "ip", source: ["udp out"] },
{ value: "kill", source: ["exec"] },
{ value: "messageExpiryInterval", source: ["mqtt"] },
{ value: "method", source: ["http request"] },
{ value: "options", source: ["xml"] },
{ value: "parts", source: ["split","join","batch","sort"] },
{ value: "pid", source: ["exec"] },
{ value: "port", source: ["tcp request"," udp out"] },
{ value: "qos", source: ["mqtt"] },
{ value: "rate", source: ["delay"] },
{ value: "rejectUnauthorized", source: ["http request"] },
{ value: "req", source: ["http in"]}, { value: "req", source: ["http in"]},
{ value: "req.body", source: ["http in"]}, { value: "req.body", source: ["http in"]},
{ value: "req.headers", source: ["http in"]}, { value: "req.headers", source: ["http in"]},
@ -100,38 +142,28 @@
{ value: "req.params", source: ["http in"]}, { value: "req.params", source: ["http in"]},
{ value: "req.cookies", source: ["http in"]}, { value: "req.cookies", source: ["http in"]},
{ value: "req.files", source: ["http in"]}, { value: "req.files", source: ["http in"]},
{ value: "complete", source: ["join"] },
{ value: "contentType", source: ["mqtt"] },
{ value: "cookies", source: ["http in","http request"] },
{ value: "correlationData", source: ["mqtt"] },
{ value: "delay", source: ["delay","trigger"] },
{ value: "encoding", source: ["file"] },
{ value: "error", source: ["catch"] },
{ value: "filename", source: ["file","file in"] },
{ value: "flush", source: ["delay"] },
{ value: "followRedirects", source: ["http request"] },
{ value: "headers", source: ["http in"," http request"] },
{ value: "kill", source: ["exec"] },
{ value: "messageExpiryInterval", source: ["mqtt"] },
{ value: "method", source: ["http-request"] },
{ value: "options", source: ["xml"] },
{ value: "parts", source: ["split","join"] },
{ value: "pid", source: ["exec"] },
{ value: "qos", source: ["mqtt"] },
{ value: "rate", source: ["delay"] },
{ value: "rejectUnauthorized", source: ["http request"] },
{ value: "requestTimeout", source: ["http request"] }, { value: "requestTimeout", source: ["http request"] },
{ value: "reset", source: ["delay","trigger","join","rbe"] }, { value: "reset", source: ["delay","trigger","join","rbe"] },
{ value: "responseCookies", source: ["http request"] },
{ value: "responseTopic", source: ["mqtt"] }, { value: "responseTopic", source: ["mqtt"] },
{ value: "responseURL", source: ["http request"] },
{ value: "restartTimeout", source: ["join"] }, { value: "restartTimeout", source: ["join"] },
{ value: "retain", source: ["mqtt"] }, { value: "retain", source: ["mqtt"] },
{ value: "schema", source: ["json"] },
{ value: "select", source: ["html"] }, { value: "select", source: ["html"] },
{ value: "statusCode", source: ["http in"] }, { value: "statusCode", source: ["http response","http request"] },
{ value: "status", source: ["status"] },
{ value: "status.text", source: ["status"] },
{ value: "status.source", source: ["status"] },
{ value: "status.source.type", source: ["status"] },
{ value: "status.source.id", source: ["status"] },
{ value: "status.source.name", source: ["status"] },
{ value: "target", source: ["link call"] },
{ value: "template", source: ["template"] }, { value: "template", source: ["template"] },
{ value: "toFront", source: ["delay"] }, { value: "toFront", source: ["delay"] },
{ value: "topic", source: ["inject","mqtt","rbe"] },
{ value: "url", source: ["http request"] }, { value: "url", source: ["http request"] },
{ value: "userProperties", source: ["mqtt"] } { value: "userProperties", source: ["mqtt"] },
{ value: "_session", source: ["websocket out","tcp out"] },
] ]
var allOptions = { var allOptions = {
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)}, msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
@ -1147,7 +1179,8 @@
this.elementDiv.show(); this.elementDiv.show();
if (opt.autoComplete) { if (opt.autoComplete) {
this.input.autoComplete({ this.input.autoComplete({
search: opt.autoComplete search: opt.autoComplete,
minLength: 0
}) })
} }
} }