Merge pull request #3521 from Steve-Mcl/autocomplete-improvements

autocomplete improvements...
This commit is contained in:
Nick O'Leary 2022-04-05 23:44:13 +01:00 committed by GitHub
commit 1be6e4565f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 104 additions and 60 deletions

View File

@ -9,12 +9,14 @@
*
* options:
*
* search : function(value, [done])
* A function that is passed the current contents of the input whenever
* it changes.
* The function must either return auto-complete options, or pass them
* to the optional 'done' parameter.
* If the function signature includes 'done', it must be used
* search: function(value, [done])
* A function that is passed the current contents of the input whenever
* it changes.
* The function must either return auto-complete options, or pass them
* to the optional 'done' parameter.
* 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:
* {
@ -26,10 +28,11 @@
$.widget( "nodered.autoComplete", {
_create: function() {
var that = this;
const that = this;
this.completionMenuShown = false;
this.options.search = this.options.search || function() { return [] }
this.element.addClass("red-ui-autoComplete")
this.options.minLength = parseInteger(this.options.minLength, 1, 0);
this.options.search = this.options.search || function() { return [] };
this.element.addClass("red-ui-autoComplete");
this.element.on("keydown.red-ui-autoComplete", function(evt) {
if ((evt.keyCode === 13 || evt.keyCode === 9) && that.completionMenuShown) {
var opts = that.menu.options();
@ -71,8 +74,8 @@
this.completionMenuShown = true;
},
_updateCompletions: function(val) {
var that = this;
if (val.trim() === "") {
const that = this;
if (val.trim().length < this.options.minLength) {
if (this.completionMenuShown) {
this.menu.hide();
}
@ -96,7 +99,7 @@
}
}
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.options.search(val,function(completions) { displayResults(completions,requestId);})
} 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);

View File

@ -55,34 +55,46 @@
}
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) {
var matches = [];
options.forEach(opt => {
let v = opt.value;
var i = v.toLowerCase().indexOf(val.toLowerCase());
if (i > -1) {
var pre = v.substring(0,i);
var matchedVal = v.substring(i,i+val.length);
var post = v.substring(i+val.length)
var el = $('<div/>',{style:"white-space:nowrap; overflow: hidden; flex-grow:1"});
$('<span/>').text(pre).appendTo(el);
$('<span/>',{style:"font-weight: bold"}).text(matchedVal).appendTo(el);
$('<span/>').text(post).appendTo(el);
var element = $('<div>',{style: "display: flex"});
el.appendTo(element);
if (opt.source) {
$('<div>').css({
"font-size": "0.8em"
}).text(opt.source.join(",")).appendTo(element);
const optVal = opt.value;
const optSrc = (opt.source||[]).join(",");
const valMatch = getMatch(optVal, val);
const srcMatch = getMatch(optSrc, val);
if (valMatch.found || srcMatch.found) {
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"});
valEl.append(generateSpans(valMatch));
valEl.appendTo(element);
if (optSrc) {
const optEl = $('<div>').css({ "font-size": "0.8em" });
optEl.append(generateSpans(srcMatch));
optEl.appendTo(element);
}
matches.push({
value: v,
label: element,
i:i
})
matches.push({
value: optVal,
label: element,
i: (valMatch.found ? valMatch.index : srcMatch.index)
});
}
})
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).
var msgCompletions = [
{ 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.body", source: ["http in"]},
{ value: "req.headers", source: ["http in"]},
@ -100,38 +142,28 @@
{ value: "req.params", source: ["http in"]},
{ value: "req.cookies", 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: "reset", source: ["delay","trigger","join","rbe"] },
{ value: "responseCookies", source: ["http request"] },
{ value: "responseTopic", source: ["mqtt"] },
{ value: "responseURL", source: ["http request"] },
{ value: "restartTimeout", source: ["join"] },
{ value: "retain", source: ["mqtt"] },
{ value: "schema", source: ["json"] },
{ 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: "toFront", source: ["delay"] },
{ value: "topic", source: ["inject","mqtt","rbe"] },
{ value: "url", source: ["http request"] },
{ value: "userProperties", source: ["mqtt"] }
{ value: "userProperties", source: ["mqtt"] },
{ value: "_session", source: ["websocket out","tcp out"] },
]
var allOptions = {
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
@ -1147,7 +1179,8 @@
this.elementDiv.show();
if (opt.autoComplete) {
this.input.autoComplete({
search: opt.autoComplete
search: opt.autoComplete,
minLength: 0
})
}
}