mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	permit config selection in subflow and subflow template
This commit is contained in:
		| @@ -326,47 +326,78 @@ RED.editor = (function() { | ||||
|  | ||||
|     /** | ||||
|      * Create a config-node select box for this property | ||||
|      * @param node - the node being edited | ||||
|      * @param property - the name of the field | ||||
|      * @param type - the type of the config-node | ||||
|      * @param  {Object} node - the node being edited | ||||
|      * @param {String} property - the name of the node property | ||||
|      * @param {String} type - the type of the config-node | ||||
|      * @param {"node-config-input"|"node-input"|"node-input-subflow-env"} prefix - the prefix to use in the input element ids | ||||
|      * @param {Function} [filter] - a function to filter the list of config nodes | ||||
|      * @param {Object} [env] - the environment variable object (only used for subflow env vars) | ||||
|      */ | ||||
|     function prepareConfigNodeSelect(node,property,type,prefix,filter) { | ||||
|         var input = $("#"+prefix+"-"+property); | ||||
|         if (input.length === 0 ) { | ||||
|     function prepareConfigNodeSelect(node, property, type, prefix, filter, env) { | ||||
|         let nodeValue | ||||
|         if (prefix === 'node-input-subflow-env') { | ||||
|             nodeValue = env?.value | ||||
|         } else { | ||||
|             nodeValue = node[property] | ||||
|         } | ||||
|  | ||||
|         const buttonId = `${prefix}-lookup-${property}` | ||||
|         const selectId = prefix + '-' + property | ||||
|         const input = $(`#${selectId}`); | ||||
|         if (input.length === 0) { | ||||
|             return; | ||||
|         } | ||||
|         var newWidth = input.width(); | ||||
|         var attrStyle = input.attr('style'); | ||||
|         var m; | ||||
|         const attrStyle = input.attr('style'); | ||||
|         let newWidth; | ||||
|         let m; | ||||
|         if ((m = /(^|\s|;)width\s*:\s*([^;]+)/i.exec(attrStyle)) !== null) { | ||||
|             newWidth = m[2].trim(); | ||||
|         } else { | ||||
|             newWidth = "70%"; | ||||
|         } | ||||
|         var outerWrap = $("<div></div>").css({ | ||||
|         const outerWrap = $("<div></div>").css({ | ||||
|             width: newWidth, | ||||
|             display:'inline-flex' | ||||
|             display: 'inline-flex' | ||||
|         }); | ||||
|         var select = $('<select id="'+prefix+'-'+property+'"></select>').appendTo(outerWrap); | ||||
|         const select = $('<select id="' + selectId + '"></select>').appendTo(outerWrap); | ||||
|         input.replaceWith(outerWrap); | ||||
|         // set the style attr directly - using width() on FF causes a value of 114%... | ||||
|         select.css({ | ||||
|             'flex-grow': 1 | ||||
|         }); | ||||
|         updateConfigNodeSelect(property,type,node[property],prefix,filter); | ||||
|         $('<a id="'+prefix+'-lookup-'+property+'" class="red-ui-button"><i class="fa fa-pencil"></i></a>') | ||||
|             .css({"margin-left":"10px"}) | ||||
|         updateConfigNodeSelect(property, type, nodeValue, prefix, filter); | ||||
|         const disableButton = function(disabled) { | ||||
|             btn.prop( "disabled", !!disabled) | ||||
|             btn.toggleClass("disabled", !!disabled) | ||||
|         } | ||||
|         // create the edit button | ||||
|         const btn = $('<a id="' + buttonId + '" class="red-ui-button"><i class="fa fa-pencil"></i></a>') | ||||
|             .css({ "margin-left": "10px" }) | ||||
|             .appendTo(outerWrap); | ||||
|         $('#'+prefix+'-lookup-'+property).on("click", function(e) { | ||||
|             showEditConfigNodeDialog(property,type,select.find(":selected").val(),prefix,node); | ||||
|  | ||||
|         // add the click handler | ||||
|         btn.on("click", function (e) { | ||||
|             const selectedOpt = select.find(":selected") | ||||
|             if (selectedOpt.data('env')) { return } // don't show the dialog for env vars items (MVP. Future enhancement: lookup the env, if present, show the associated edit dialog) | ||||
|             if (btn.prop("disabled")) { return } | ||||
|             showEditConfigNodeDialog(property, type, selectedOpt.val(), prefix, node); | ||||
|             e.preventDefault(); | ||||
|         }); | ||||
|  | ||||
|         // dont permit the user to click the button if the selected option is an env var | ||||
|         select.on("change", function () { | ||||
|             const selectedOpt = select.find(":selected") | ||||
|             if (selectedOpt?.data('env')) { | ||||
|                 disableButton(true) | ||||
|             } else { | ||||
|                 disableButton(false) | ||||
|             } | ||||
|         }); | ||||
|         var label = ""; | ||||
|         var configNode = RED.nodes.node(node[property]); | ||||
|         var node_def = RED.nodes.getType(type); | ||||
|         var configNode = RED.nodes.node(nodeValue); | ||||
|  | ||||
|         if (configNode) { | ||||
|             label = RED.utils.getNodeLabel(configNode,configNode.id); | ||||
|             label = RED.utils.getNodeLabel(configNode, configNode.id); | ||||
|         } | ||||
|         input.val(label); | ||||
|     } | ||||
| @@ -768,12 +799,9 @@ RED.editor = (function() { | ||||
|     } | ||||
|  | ||||
|     function defaultConfigNodeSort(A,B) { | ||||
|         if (A.__label__ < B.__label__) { | ||||
|             return -1; | ||||
|         } else if (A.__label__ > B.__label__) { | ||||
|             return 1; | ||||
|         } | ||||
|         return 0; | ||||
|         // sort case insensitive so that `[env] node-name` items are at the top and | ||||
|         // not mixed inbetween the the lower and upper case items | ||||
|         return (A.__label__ || '').localeCompare((B.__label__ || ''), undefined, {sensitivity: 'base'}) | ||||
|     } | ||||
|  | ||||
|     function updateConfigNodeSelect(name,type,value,prefix,filter) { | ||||
| @@ -788,7 +816,7 @@ RED.editor = (function() { | ||||
|                 } | ||||
|                 $("#"+prefix+"-"+name).val(value); | ||||
|             } else { | ||||
|  | ||||
|                 let inclSubflowEnvvars = false | ||||
|                 var select = $("#"+prefix+"-"+name); | ||||
|                 var node_def = RED.nodes.getType(type); | ||||
|                 select.children().remove(); | ||||
| @@ -796,6 +824,7 @@ RED.editor = (function() { | ||||
|                 var activeWorkspace = RED.nodes.workspace(RED.workspaces.active()); | ||||
|                 if (!activeWorkspace) { | ||||
|                     activeWorkspace = RED.nodes.subflow(RED.workspaces.active()); | ||||
|                     inclSubflowEnvvars = true | ||||
|                 } | ||||
|  | ||||
|                 var configNodes = []; | ||||
| @@ -811,6 +840,31 @@ RED.editor = (function() { | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|   | ||||
|                 // as includeSubflowEnvvars is true, this is a subflow. | ||||
|                 // include any 'conf-types' env vars as a list of avaiable configs | ||||
|                 // in the config dropdown as `[env] node-name` | ||||
|                 if (inclSubflowEnvvars && activeWorkspace.env) { | ||||
|                     const parentEnv = activeWorkspace.env.filter(env => env.ui?.type === 'conf-types' && env.type === type) | ||||
|                     if (parentEnv && parentEnv.length > 0) { | ||||
|                         const locale = RED.i18n.lang() | ||||
|                         for (let i = 0; i < parentEnv.length; i++) { | ||||
|                             const tenv = parentEnv[i] | ||||
|                             const ui = tenv.ui || {} | ||||
|                             const labels = ui.label || {} | ||||
|                             const labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"] || tenv.name, locale) | ||||
|                             const config = { | ||||
|                                 env: tenv, | ||||
|                                 id: '${' + parentEnv[0].name + '}', | ||||
|                                 type: type, | ||||
|                                 label: labelText, | ||||
|                                 __label__: `[env] ${labelText}` | ||||
|                             } | ||||
|                             configNodes.push(config) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 var configSortFn = defaultConfigNodeSort; | ||||
|                 if (typeof node_def.sort == "function") { | ||||
|                     configSortFn = node_def.sort; | ||||
| @@ -822,7 +876,10 @@ RED.editor = (function() { | ||||
|                 } | ||||
|  | ||||
|                 configNodes.forEach(function(cn) { | ||||
|                     $('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select); | ||||
|                     const option = $('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select); | ||||
|                     if (cn.env) { | ||||
|                         option.data('env', cn.env) // set a data attribute to indicate this is an env var (to inhibit the edit button) | ||||
|                     } | ||||
|                     delete cn.__label__; | ||||
|                 }); | ||||
|  | ||||
| @@ -1478,9 +1535,16 @@ RED.editor = (function() { | ||||
|                     } | ||||
|                     RED.tray.close(function() { | ||||
|                         var filter = null; | ||||
|                         if (editContext && typeof editContext._def.defaults[configProperty].filter === 'function') { | ||||
|                             filter = function(n) { | ||||
|                                 return editContext._def.defaults[configProperty].filter.call(editContext,n); | ||||
|                         // when editing a config via subflow edit panel, the `configProperty` will not | ||||
|                         // necessarily be a property of the editContext._def.defaults object | ||||
|                         // Also, when editing via dashboard sidebar, editContext can be null | ||||
|                         // so we need to guard both scenarios | ||||
|                         if (editContext?._def) { | ||||
|                             const isSubflow = (editContext._def.type === 'subflow' || /subflow:.*/.test(editContext._def.type)) | ||||
|                             if (editContext && !isSubflow && typeof editContext._def.defaults?.[configProperty]?.filter === 'function') { | ||||
|                                 filter = function(n) { | ||||
|                                     return editContext._def.defaults[configProperty].filter.call(editContext,n); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix,filter); | ||||
| @@ -1541,7 +1605,7 @@ RED.editor = (function() { | ||||
|                     RED.history.push(historyEvent); | ||||
|                     RED.tray.close(function() { | ||||
|                         var filter = null; | ||||
|                         if (editContext && typeof editContext._def.defaults[configProperty].filter === 'function') { | ||||
|                         if (editContext && typeof editContext._def.defaults[configProperty]?.filter === 'function') { | ||||
|                             filter = function(n) { | ||||
|                                 return editContext._def.defaults[configProperty].filter.call(editContext,n); | ||||
|                             } | ||||
| @@ -2127,6 +2191,7 @@ RED.editor = (function() { | ||||
|                 filteredEditPanes[type] = filter | ||||
|             } | ||||
|             editPanes[type] = definition; | ||||
|         } | ||||
|         }, | ||||
|         prepareConfigNodeSelect: prepareConfigNodeSelect, | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| RED.editor.envVarList = (function() { | ||||
|  | ||||
|     var currentLocale = 'en-US'; | ||||
|     var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env']; | ||||
|     var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata']; | ||||
|     const DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env']; | ||||
|     const DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES = ['str','num','bool','json','bin','env','conf-types']; | ||||
|     const DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata']; | ||||
|  | ||||
|     /** | ||||
|      * Create env var edit interface | ||||
| @@ -10,8 +11,8 @@ RED.editor.envVarList = (function() { | ||||
|      * @param node - subflow node | ||||
|      */ | ||||
|     function buildPropertiesList(envContainer, node) { | ||||
|  | ||||
|         var isTemplateNode = (node.type === "subflow"); | ||||
|         if(RED.editor.envVarList.debug) { console.log('envVarList: buildPropertiesList', envContainer, node) } | ||||
|         const isTemplateNode = (node.type === "subflow"); | ||||
|  | ||||
|         envContainer | ||||
|             .css({ | ||||
| @@ -83,7 +84,14 @@ RED.editor.envVarList = (function() { | ||||
|                         // if `opt.ui` does not exist, then apply defaults. If these | ||||
|                         // defaults do not change then they will get stripped off | ||||
|                         // before saving. | ||||
|                         if (opt.type === 'cred') { | ||||
|                         if (opt.type === 'conf-types') { | ||||
|                             opt.ui = opt.ui || { | ||||
|                                 icon: "fa fa-cog", | ||||
|                                 type: "conf-types", | ||||
|                                 opts: {opts:[]} | ||||
|                             } | ||||
|                             opt.ui.type = "conf-types"; | ||||
|                         } else if (opt.type === 'cred') { | ||||
|                             opt.ui = opt.ui || { | ||||
|                                 icon: "", | ||||
|                                 type: "cred" | ||||
| @@ -119,7 +127,7 @@ RED.editor.envVarList = (function() { | ||||
|                             } | ||||
|                         }); | ||||
|  | ||||
|                         buildEnvEditRow(uiRow, opt.ui, nameField, valueField); | ||||
|                         buildEnvEditRow(uiRow, opt, nameField, valueField); | ||||
|                         nameField.trigger('change'); | ||||
|                     } | ||||
|                 }, | ||||
| @@ -181,21 +189,23 @@ RED.editor.envVarList = (function() { | ||||
|      * @param nameField - name field of env var | ||||
|      * @param valueField - value field of env var | ||||
|      */ | ||||
|      function buildEnvEditRow(container, ui, nameField, valueField) { | ||||
|      function buildEnvEditRow(container, opt, nameField, valueField) { | ||||
|         const ui = opt.ui | ||||
|         if(RED.editor.envVarList.debug) { console.log('envVarList: buildEnvEditRow', container, ui, nameField, valueField) } | ||||
|          container.addClass("red-ui-editor-subflow-env-ui-row") | ||||
|          var topRow = $('<div></div>').appendTo(container); | ||||
|          $('<div></div>').appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.icon")).appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.label")).appendTo(topRow); | ||||
|          $('<div>').text(RED._("editor.inputType")).appendTo(topRow); | ||||
|          $('<div class="red-env-ui-input-type-col">').text(RED._("editor.inputType")).appendTo(topRow); | ||||
|  | ||||
|          var row = $('<div></div>').appendTo(container); | ||||
|          $('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row); | ||||
|          var typeOptions = { | ||||
|              'input': {types:DEFAULT_ENV_TYPE_LIST}, | ||||
|              'select': {opts:[]}, | ||||
|              'spinner': {}, | ||||
|              'cred': {} | ||||
|             'input': {types:DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES}, | ||||
|             'select': {opts:[]}, | ||||
|             'spinner': {}, | ||||
|             'cred': {} | ||||
|          }; | ||||
|          if (ui.opts) { | ||||
|              typeOptions[ui.type] = ui.opts; | ||||
| @@ -260,15 +270,16 @@ RED.editor.envVarList = (function() { | ||||
|             labelInput.attr("placeholder",$(this).val()) | ||||
|         }); | ||||
|  | ||||
|         var inputCell = $('<div></div>').appendTo(row); | ||||
|         var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell); | ||||
|         var inputCell = $('<div class="red-env-ui-input-type-col"></div>').appendTo(row); | ||||
|         var uiInputTypeInput = $('<input type="text">').css("width","100%").appendTo(inputCell); | ||||
|         if (ui.type === "input") { | ||||
|             inputCellInput.val(ui.opts.types.join(",")); | ||||
|             uiInputTypeInput.val(ui.opts.types.join(",")); | ||||
|         } | ||||
|         var checkbox; | ||||
|         var selectBox; | ||||
|  | ||||
|         inputCellInput.typedInput({ | ||||
|         // the options presented in the UI section for an "input" type selection | ||||
|         uiInputTypeInput.typedInput({ | ||||
|             types: [ | ||||
|                 { | ||||
|                     value:"input", | ||||
| @@ -429,7 +440,7 @@ RED.editor.envVarList = (function() { | ||||
|                                         } | ||||
|                                     }); | ||||
|                                     ui.opts.opts = vals; | ||||
|                                     inputCellInput.typedInput('value',Date.now()) | ||||
|                                     uiInputTypeInput.typedInput('value',Date.now()) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| @@ -496,12 +507,13 @@ RED.editor.envVarList = (function() { | ||||
|                                     } else { | ||||
|                                         delete ui.opts.max; | ||||
|                                     } | ||||
|                                     inputCellInput.typedInput('value',Date.now()) | ||||
|                                     uiInputTypeInput.typedInput('value',Date.now()) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 'conf-types', | ||||
|                 { | ||||
|                     value:"none", | ||||
|                     label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false | ||||
| @@ -519,14 +531,20 @@ RED.editor.envVarList = (function() { | ||||
|                 // In the case of 'input' type, the typedInput uses the multiple-option | ||||
|                 // mode. Its value needs to be set to a comma-separately list of the | ||||
|                 // selected options. | ||||
|                 inputCellInput.typedInput('value',ui.opts.types.join(",")) | ||||
|                 uiInputTypeInput.typedInput('value',ui.opts.types.join(",")) | ||||
|             } else if (ui.type === 'conf-types') { | ||||
|                 // In the case of 'conf-types' type, the typedInput will be populated | ||||
|                 // with a list of all config nodes types installed. | ||||
|                 // Restore the value to the last selected type | ||||
|                 uiInputTypeInput.typedInput('value', opt.type) | ||||
|             } else { | ||||
|                 // No other type cares about `value`, but doing this will | ||||
|                 // force a refresh of the label now that `ui.opts` has | ||||
|                 // been updated. | ||||
|                 inputCellInput.typedInput('value',Date.now()) | ||||
|                 uiInputTypeInput.typedInput('value',Date.now()) | ||||
|             } | ||||
|  | ||||
|             if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:typedinputtypechange. ui.type = ' + ui.type) } | ||||
|             switch (ui.type) { | ||||
|                 case 'input': | ||||
|                     valueField.typedInput('types',ui.opts.types); | ||||
| @@ -544,7 +562,7 @@ RED.editor.envVarList = (function() { | ||||
|                     valueField.typedInput('types',['cred']); | ||||
|                     break; | ||||
|                 default: | ||||
|                     valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST) | ||||
|                     valueField.typedInput('types', DEFAULT_ENV_TYPE_LIST); | ||||
|             } | ||||
|             if (ui.type === 'checkbox') { | ||||
|                 valueField.typedInput('type','bool'); | ||||
| @@ -556,8 +574,46 @@ RED.editor.envVarList = (function() { | ||||
|             } | ||||
|  | ||||
|         }).on("change", function(evt,type) { | ||||
|             if (ui.type === 'input') { | ||||
|                 var types = inputCellInput.typedInput('value'); | ||||
|             const selectedType = $(this).typedInput('type') // the UI typedInput type | ||||
|             if(RED.editor.envVarList.debug || true) { console.log('envVarList: inputCellInput on:change. selectedType = ' + selectedType) } | ||||
|             if (selectedType === 'conf-types') { | ||||
|                 const selectedConfigType = $(this).typedInput('value') || opt.type | ||||
|                 let activeWorkspace = RED.nodes.workspace(RED.workspaces.active()); | ||||
|                 if (!activeWorkspace) { | ||||
|                     activeWorkspace = RED.nodes.subflow(RED.workspaces.active()); | ||||
|                 } | ||||
|  | ||||
|                 // get a list of all config nodes matching the selectedValue | ||||
|                 const configNodes = []; | ||||
|                 RED.nodes.eachConfig(function(config) { | ||||
|                     if (config.type == selectedConfigType && (!config.z || config.z === activeWorkspace.id)) { | ||||
|                         const modulePath = config._def?.set?.id || '' | ||||
|                         let label = RED.utils.getNodeLabel(config, config.id) || config.id; | ||||
|                         label += config.d ? ' ['+RED._('workspace.disabled')+']' : ''; | ||||
|                         const _config = { | ||||
|                             _type: selectedConfigType, | ||||
|                             value: config.id, | ||||
|                             label: label, | ||||
|                             title: modulePath ? modulePath + ' - ' + label : label, | ||||
|                             enabled: config.d !== true, | ||||
|                             disabled: config.d === true, | ||||
|                         } | ||||
|                         configNodes.push(_config); | ||||
|                     } | ||||
|                 }); | ||||
|                 const tiTypes = { | ||||
|                     value: selectedConfigType, | ||||
|                     label: "config", | ||||
|                     icon: "fa fa-cog", | ||||
|                     options: configNodes, | ||||
|                 } | ||||
|                 valueField.typedInput('types', [tiTypes]); | ||||
|                 valueField.typedInput('type', selectedConfigType); | ||||
|                 valueField.typedInput('value', opt.value); | ||||
|  | ||||
|  | ||||
|             } else if (ui.type === 'input') { | ||||
|                 var types = uiInputTypeInput.typedInput('value'); | ||||
|                 ui.opts.types = (types === "") ? ["str"] : types.split(","); | ||||
|                 valueField.typedInput('types',ui.opts.types); | ||||
|             } | ||||
| @@ -569,7 +625,7 @@ RED.editor.envVarList = (function() { | ||||
|         }) | ||||
|         // Set the input to the right type. This will trigger the 'typedinputtypechange' | ||||
|         // event handler (just above ^^) to update the value if needed | ||||
|         inputCellInput.typedInput('type',ui.type) | ||||
|         uiInputTypeInput.typedInput('type',ui.type) | ||||
|     } | ||||
|  | ||||
|     function setLocale(l, list) { | ||||
|   | ||||
| @@ -909,17 +909,19 @@ RED.subflow = (function() { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Create interface for controlling env var UI definition | ||||
|      * Build the edit dialog for a subflow template (creating/modifying a subflow template) | ||||
|      * @param {Object} uiContainer - the jQuery container for the environment variable list | ||||
|      * @param {Object} node - the subflow template node | ||||
|      */ | ||||
|     function buildEnvControl(envList,node) { | ||||
|     function buildEnvControl(uiContainer,node) { | ||||
|         var tabs = RED.tabs.create({ | ||||
|             id: "subflow-env-tabs", | ||||
|             onchange: function(tab) { | ||||
|                 if (tab.id === "subflow-env-tab-preview") { | ||||
|                     var inputContainer = $("#subflow-input-ui"); | ||||
|                     var list = envList.editableList("items"); | ||||
|                     var list = uiContainer.editableList("items"); | ||||
|                     var exportedEnv = exportEnvList(list, true); | ||||
|                     buildEnvUI(inputContainer, exportedEnv,node); | ||||
|                     buildEnvUI(inputContainer, exportedEnv, node); | ||||
|                 } | ||||
|                 $("#subflow-env-tabs-content").children().hide(); | ||||
|                 $("#" + tab.id).show(); | ||||
| @@ -957,12 +959,33 @@ RED.subflow = (function() { | ||||
|         RED.editor.envVarList.setLocale(locale); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function buildEnvUIRow(row, tenv, ui, node) { | ||||
|     /** | ||||
|      * Build a UI row for a subflow instance environment variable | ||||
|      * Also used to build the UI row for subflow template preview | ||||
|      * @param {JQuery} row - A form row element | ||||
|      * @param {Object} tenv - A template environment variable | ||||
|      * @param {String} tenv.name - The name of the environment variable | ||||
|      * @param {String} tenv.type - The type of the environment variable | ||||
|      * @param {String} tenv.value - The value set for this environment variable | ||||
|      * @param {Object} tenv.parent - The parent environment variable | ||||
|      * @param {String} tenv.parent.value - The value set for the parent environment variable | ||||
|      * @param {String} tenv.parent.type - The type of the parent environment variable | ||||
|      * @param {Object} tenv.ui - The UI configuration for the environment variable | ||||
|      * @param {String} tenv.ui.icon - The icon for the environment variable | ||||
|      * @param {Object} tenv.ui.label - The label for the environment variable | ||||
|      * @param {String} tenv.ui.type - The type of the UI control for the environment variable | ||||
|      * @param {Object} node - The subflow instance node | ||||
|      */ | ||||
|     function buildEnvUIRow(row, tenv, node) { | ||||
|         if(RED.subflow.debug) { console.log("buildEnvUIRow", tenv) } | ||||
|         const ui = tenv.ui || {} | ||||
|         ui.label = ui.label||{}; | ||||
|         if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) { | ||||
|             ui.type = "cred"; | ||||
|             ui.opts = {}; | ||||
|         } else if (tenv.type === "conf-types") { | ||||
|             ui.type = "conf-types" | ||||
|             ui.opts = { types: ['conf-types'] } | ||||
|         } else if (!ui.type) { | ||||
|             ui.type = "input"; | ||||
|             ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST } | ||||
| @@ -1006,9 +1029,10 @@ RED.subflow = (function() { | ||||
|         if (tenv.hasOwnProperty('type')) { | ||||
|             val.type = tenv.type; | ||||
|         } | ||||
|         const elId = getSubflowEnvPropertyName(tenv.name) | ||||
|         switch(ui.type) { | ||||
|             case "input": | ||||
|                 input = $('<input type="text">').css('width','70%').appendTo(row); | ||||
|                 input = $('<input type="text">').css('width','70%').attr('id', elId).appendTo(row); | ||||
|                 if (ui.opts.types && ui.opts.types.length > 0) { | ||||
|                     var inputType = val.type; | ||||
|                     if (ui.opts.types.indexOf(inputType) === -1) { | ||||
| @@ -1035,7 +1059,7 @@ RED.subflow = (function() { | ||||
|                 } | ||||
|                 break; | ||||
|             case "select": | ||||
|                 input = $('<select>').css('width','70%').appendTo(row); | ||||
|                 input = $('<select>').css('width','70%').attr('id', elId).appendTo(row); | ||||
|                 if (ui.opts.opts) { | ||||
|                     ui.opts.opts.forEach(function(o) { | ||||
|                         $('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input); | ||||
| @@ -1046,7 +1070,7 @@ RED.subflow = (function() { | ||||
|             case "checkbox": | ||||
|                 label.css("cursor","default"); | ||||
|                 var cblabel = $('<label>').css('width','70%').appendTo(row); | ||||
|                 input = $('<input type="checkbox">').css({ | ||||
|                 input = $('<input type="checkbox">').attr('id', elId).css({ | ||||
|                     marginTop: 0, | ||||
|                     width: 'auto', | ||||
|                     height: '34px' | ||||
| @@ -1064,7 +1088,7 @@ RED.subflow = (function() { | ||||
|                 input.prop("checked",boolVal); | ||||
|                 break; | ||||
|             case "spinner": | ||||
|                 input = $('<input>').css('width','70%').appendTo(row); | ||||
|                 input = $('<input>').css('width','70%').attr('id', elId).appendTo(row); | ||||
|                 var spinnerOpts = {}; | ||||
|                 if (ui.opts.hasOwnProperty('min')) { | ||||
|                     spinnerOpts.min = ui.opts.min; | ||||
| @@ -1093,18 +1117,25 @@ RED.subflow = (function() { | ||||
|                     default: 'cred' | ||||
|                 }) | ||||
|                 break; | ||||
|         } | ||||
|         if (input) { | ||||
|             input.attr('id',getSubflowEnvPropertyName(tenv.name)) | ||||
|             case "conf-types": | ||||
|                 // let clsId = 'config-node-input-' + val.type + '-' + val.value + '-' + Math.floor(Math.random() * 100000); | ||||
|                 // clsId = clsId.replace(/\W/g, '-'); | ||||
|                 // input = $('<input>').css('width','70%').addClass(clsId).attr('id', elId).appendTo(row); | ||||
|                 input = $('<input>').css('width','70%').attr('id', elId).appendTo(row); | ||||
|                 const _type = tenv.parent?.type || tenv.type; | ||||
|                 RED.editor.prepareConfigNodeSelect(node, tenv.name, _type, 'node-input-subflow-env', null, tenv); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create environment variable input UI | ||||
|      * Build the edit form for a subflow instance | ||||
|      * Also used to build the preview form in the subflow template edit dialog | ||||
|      * @param uiContainer - container for UI | ||||
|      * @param envList - env var definitions of template | ||||
|      */ | ||||
|     function buildEnvUI(uiContainer, envList, node) { | ||||
|         if(RED.subflow.debug) { console.log("buildEnvUI",envList) } | ||||
|         uiContainer.empty(); | ||||
|         for (var i = 0; i < envList.length; i++) { | ||||
|             var tenv = envList[i]; | ||||
| @@ -1112,7 +1143,7 @@ RED.subflow = (function() { | ||||
|                 continue; | ||||
|             } | ||||
|             var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer); | ||||
|             buildEnvUIRow(row,tenv, tenv.ui || {}, node); | ||||
|             buildEnvUIRow(row, tenv, node); | ||||
|         } | ||||
|     } | ||||
|     // buildEnvUI | ||||
| @@ -1185,6 +1216,8 @@ RED.subflow = (function() { | ||||
|                                         delete ui.opts | ||||
|                                     } | ||||
|                                     break; | ||||
|                                 case "conf-types": | ||||
|                                     delete ui.opts; // CHECK ME DOESNT BREAK STUFF | ||||
|                                 default: | ||||
|                                     delete ui.opts; | ||||
|                             } | ||||
| @@ -1207,8 +1240,9 @@ RED.subflow = (function() { | ||||
|         if (/^subflow:/.test(node.type)) { | ||||
|             var subflowDef = RED.nodes.subflow(node.type.substring(8)); | ||||
|             if (subflowDef.env) { | ||||
|                 subflowDef.env.forEach(function(env) { | ||||
|                 subflowDef.env.forEach(function(env, i) { | ||||
|                     var item = { | ||||
|                         index: i, | ||||
|                         name:env.name, | ||||
|                         parent: { | ||||
|                             type: env.type, | ||||
| @@ -1273,6 +1307,7 @@ RED.subflow = (function() { | ||||
|     } | ||||
|  | ||||
|     function exportSubflowInstanceEnv(node) { | ||||
|         if(RED.subflow.debug) { console.log("exportSubflowInstanceEnv",node) } | ||||
|         var env = []; | ||||
|         // First, get the values for the SubflowTemplate defined properties | ||||
|         //  - these are the ones with custom UI elements | ||||
| @@ -1319,6 +1354,9 @@ RED.subflow = (function() { | ||||
|                         item.type = 'bool'; | ||||
|                         item.value = ""+input.prop("checked"); | ||||
|                         break; | ||||
|                     case "conf-types": | ||||
|                         item.value = input.val() | ||||
|                         item.type = data.parent.value; | ||||
|                 } | ||||
|                 if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) { | ||||
|                     env.push(item); | ||||
| @@ -1332,8 +1370,15 @@ RED.subflow = (function() { | ||||
|         return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_"); | ||||
|     } | ||||
|  | ||||
|     // Called by subflow.oneditprepare for both instances and templates | ||||
|      | ||||
|     /** | ||||
|      * Build the subflow edit form | ||||
|      * Called by subflow.oneditprepare for both instances and templates | ||||
|      * @param {"subflow"|"subflow-template"} type - the type of subflow being edited | ||||
|      * @param {Object} node - the node being edited | ||||
|      */ | ||||
|     function buildEditForm(type,node) { | ||||
|         if(RED.subflow.debug) { console.log("buildEditForm",type,node) } | ||||
|         if (type === "subflow-template") { | ||||
|             // This is the tabbed UI that offers the env list - with UI options | ||||
|             // plus the preview tab | ||||
|   | ||||
		Reference in New Issue
	
	Block a user