From 475113838a299973a53cfdd023578c5567e93300 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Tue, 5 Apr 2022 14:00:14 +0100 Subject: [PATCH] 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 --- .../src/js/ui/common/autoComplete.js | 35 +++-- .../src/js/ui/common/typedInput.js | 129 +++++++++++------- 2 files changed, 104 insertions(+), 60 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js index 192ccda7a..a3ce0bcd1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js @@ -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); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index 2d628769e..1d649d6e0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -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($('').text(match.pre)); } + if(match.match) { els.push($('',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); } + if(match.post) { els.push($('').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 = $('
',{style:"white-space:nowrap; overflow: hidden; flex-grow:1"}); - $('').text(pre).appendTo(el); - $('',{style:"font-weight: bold"}).text(matchedVal).appendTo(el); - $('').text(post).appendTo(el); - - var element = $('
',{style: "display: flex"}); - el.appendTo(element); - if (opt.source) { - $('
').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 = $('
',{style: "display: flex"}); + const valEl = $('
',{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 = $('
').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 }) } }