From 741fe3dd90d773f4b7f38ce9320ad1e169795679 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 2 Sep 2021 14:29:58 +0100 Subject: [PATCH] Move tab edit dialog into editor and use new edit panes --- .../editor-client/src/js/ui/editor.js | 135 ++++++++- ...bflowProperties.js => envVarProperties.js} | 5 +- .../src/js/ui/editors/panes/flowProperties.js | 63 ++++ .../editor-client/src/js/ui/group.js | 2 - .../editor-client/src/js/ui/subflow.js | 49 ++- .../editor-client/src/js/ui/workspaces.js | 281 +----------------- 6 files changed, 225 insertions(+), 310 deletions(-) rename packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/{subflowProperties.js => envVarProperties.js} (94%) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index a2841cdde..3b61b861a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -529,7 +529,7 @@ RED.editor = (function() { done(activeEditPanes); } } - if (definition.credentials || /^subflow:/.test(definition.type) || (node.type === "group")) { + if (definition.credentials || /^subflow:/.test(definition.type) || node.type === "group" || node.type === "tab") { if (node.credentials) { populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); completePrepare(); @@ -1646,6 +1646,7 @@ RED.editor = (function() { var nodeEditPanes = [ 'editor-tab-properties', + 'editor-tab-envProperties', 'editor-tab-description' ]; prepareEditDialog(trayBody, nodeEditPanes, group,group._def,"node-input", null, function(_activeEditPanes) { @@ -1679,6 +1680,137 @@ RED.editor = (function() { RED.tray.show(trayOptions); } + function showEditFlowDialog(workspace) { + if (buildingEditDialog) { return } + buildingEditDialog = true; + var activeEditPanes = []; + RED.view.state(RED.state.EDITING); + var trayOptions = { + title: RED._("workspace.editFlow",{name:RED.utils.sanitize(workspace.label)}), + buttons: [ + { + id: "node-dialog-delete", + class: 'leftButton'+((RED.workspaces.count() === 1)?" disabled":""), + text: RED._("common.label.delete"), //'', + click: function() { + RED.workspaces.delete(workspace); + RED.tray.close(); + } + }, + { + id: "node-dialog-cancel", + text: RED._("common.label.cancel"), + click: function() { + RED.tray.close(); + } + }, + { + id: "node-dialog-ok", + class: "primary", + text: RED._("common.label.done"), + click: function() { + var editState = { + changes: {}, + changed: false, + outputMap: null + } + var wasDirty = RED.nodes.dirty(); + + activeEditPanes.forEach(function(pane) { + if (pane.apply) { + pane.apply.call(pane, workspace, editState); + } + }) + + var disabled = $("#node-input-disabled").prop("checked"); + if (workspace.disabled !== disabled) { + editState.changes.disabled = workspace.disabled; + editState.changed = true; + workspace.disabled = disabled; + } + + + if (editState.changed) { + var historyEvent = { + t: "edit", + changes: editState.changes, + node: workspace, + dirty: wasDirty + } + workspace.changed = true; + RED.history.push(historyEvent); + RED.nodes.dirty(true); + if (editState.changes.hasOwnProperty('disabled')) { + RED.nodes.eachNode(function(n) { + if (n.z === workspace.id) { + n.dirty = true; + } + }); + RED.view.redraw(); + } + RED.workspaces.refresh(); + RED.events.emit("flows:change",workspace); + } + RED.tray.close(); + } + } + ], + resize: function(dimensions) { + $(".red-ui-tray-content").height(dimensions.height - 50); + var form = $(".red-ui-tray-content form").height(dimensions.height - 50 - 40); + var size = {width:form.width(),height:form.height()}; + activeEditPanes.forEach(function(pane) { + if (pane.resize) { + pane.resize.call(pane,editing_node, size); + } + }) + }, + open: function(tray, done) { + var trayFooter = tray.find(".red-ui-tray-footer"); + var trayBody = tray.find('.red-ui-tray-body'); + trayBody.parent().css('overflow','hidden'); + var trayFooterLeft = $('').appendTo(trayFooter) + + var nodeEditPanes = [ + 'editor-tab-flow-properties', + 'editor-tab-envProperties' + ]; + + if (!workspace.hasOwnProperty("disabled")) { + workspace.disabled = false; + } + $('').prop("checked",workspace.disabled).appendTo(trayFooterLeft).toggleButton({ + enabledIcon: "fa-circle-thin", + disabledIcon: "fa-ban", + invertState: true + }) + + prepareEditDialog(trayBody, nodeEditPanes, workspace, {}, "node-input", null, function(_activeEditPanes) { + activeEditPanes = _activeEditPanes; + trayBody.i18n(); + trayFooter.i18n(); + buildingEditDialog = false; + done(); + }); + }, + close: function() { + if (RED.view.state() != RED.state.IMPORT_DRAGGING) { + RED.view.state(RED.state.DEFAULT); + } + activeEditPanes.forEach(function(pane) { + if (pane.close) { + pane.close.call(pane,editing_node); + } + }) + var selection = RED.view.selection(); + if (!selection.nodes && !selection.links && workspace.id === RED.workspaces.active()) { + RED.sidebar.info.refresh(workspace); + } + } + } + RED.tray.show(trayOptions); + } + function showTypeEditor(type, options) { if (customEditTypes.hasOwnProperty(type)) { if (editStack.length > 0) { @@ -1713,6 +1845,7 @@ RED.editor = (function() { }, edit: showEditDialog, editConfig: showEditConfigNodeDialog, + editFlow: showEditFlowDialog, editSubflow: showEditSubflowDialog, editGroup: showEditGroupDialog, editJavaScript: function(options) { showTypeEditor("_js",options) }, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowProperties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js similarity index 94% rename from packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowProperties.js rename to packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js index f9071fac8..9d6fda1ea 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowProperties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js @@ -21,7 +21,10 @@ }, apply: function(node, editState) { var old_env = node.env; - var new_env = RED.subflow.exportSubflowInstanceEnv(node); + var new_env = []; + if (/^subflow:/.test(node.type)) { + new_env = RED.subflow.exportSubflowInstanceEnv(node); + } // Get the values from the Properties table tab var items = this.list.editableList('items'); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js new file mode 100644 index 000000000..c72eed7a6 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js @@ -0,0 +1,63 @@ +;(function() { + + RED.editor.registerEditorPane("editor-tab-flow-properties", function() { + return { + label: RED._("editor-tab.properties"), + name: RED._("editor-tab.properties"), + iconClass: "fa fa-cog", + create: function(container, node) { + var content = $('
', {class:"red-ui-tray-content"}).appendTo(container); + + var dialogForm = $('
').appendTo(content); + $('
'+ + ''+ + ''+ + '
').appendTo(dialogForm); + + var row = $('
'+ + ''+ + '
'+ + '
').appendTo(dialogForm); + this.tabflowEditor = RED.editor.createEditor({ + id: 'node-input-info', + mode: 'ace/mode/markdown', + value: "" + }); + + $('').prependTo(dialogForm); + dialogForm.on("submit", function(e) { e.preventDefault();}); + + $("#node-input-name").val(node.label); + RED.text.bidi.prepareInput($("#node-input-name")); + this.tabflowEditor.getSession().setValue(node.info || "", -1); + return content; + }, + resize: function(node, size) { + $("#node-input-info").css("height", (size.height-70)+"px"); + this.tabflowEditor.resize(); + }, + close: function(node) { + this.tabflowEditor.destroy(); + }, + apply: function(workspace, editState) { + var label = $( "#node-input-name" ).val(); + + if (workspace.label != label) { + editState.changes.label = workspace.label; + editState.changed = true; + workspace.label = label; + } + + var info = this.tabflowEditor.getValue(); + if (workspace.info !== info) { + editState.changes.info = workspace.info; + editState.changed = true; + workspace.info = info; + } + $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled); + $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); + + } + } + }); +})(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js index 114bb7207..27b5f9f5a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js @@ -97,8 +97,6 @@ RED.group = (function() { }, category: "config", oneditprepare: function() { - RED.subflow.buildPropertiesForm(this); - var style = this.style || {}; RED.editor.colorPicker.create({ id:"node-input-style-stroke", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index de40ea939..c236e4f89 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -948,9 +948,6 @@ RED.subflow = (function() { function buildPropertiesList(envContainer, node) { var isTemplateNode = (node.type === "subflow"); - var isGroup = (node.type === "group"); - var isTab = (node.type === "tab"); - var kind = isGroup ? "group" : (isTab ? "tab" : "subflow"); if (isTemplateNode) { buildEnvControl(envContainer, node); @@ -1812,30 +1809,26 @@ RED.subflow = (function() { function exportSubflowInstanceEnv(node) { var env = []; - var isGroup = (node.type === "group"); - var isTab = (node.type === "tab"); - - if (!isGroup && !isTab) { - // First, get the values for the SubflowTemplate defined properties - // - these are the ones with custom UI elements - var parentEnv = getSubflowInstanceParentEnv(node); - parentEnv.forEach(function(data) { - var item; - var ui = data.ui || {}; - if (!ui.type) { - if (data.parent && data.parent.type === "cred") { - ui.type = "cred"; - } else { - ui.type = "input"; - ui.opts = {types:DEFAULT_ENV_TYPE_LIST} - } + // First, get the values for the SubflowTemplate defined properties + // - these are the ones with custom UI elements + var parentEnv = getSubflowInstanceParentEnv(node); + parentEnv.forEach(function(data) { + var item; + var ui = data.ui || {}; + if (!ui.type) { + if (data.parent && data.parent.type === "cred") { + ui.type = "cred"; } else { - ui.opts = ui.opts || {}; + ui.type = "input"; + ui.opts = {types:DEFAULT_ENV_TYPE_LIST} } - var input = $("#"+getSubflowEnvPropertyName(data.name)); - if (input.length || ui.type === "cred") { - item = { name: data.name }; - switch(ui.type) { + } else { + ui.opts = ui.opts || {}; + } + var input = $("#"+getSubflowEnvPropertyName(data.name)); + if (input.length || ui.type === "cred") { + item = { name: data.name }; + switch(ui.type) { case "input": if (ui.opts.types && ui.opts.types.length > 0) { item.value = input.typedInput('value'); @@ -1861,10 +1854,6 @@ RED.subflow = (function() { item.type = 'bool'; item.value = ""+input.prop("checked"); break; - } - if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) { - env.push(item); - } } if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) { env.push(item); @@ -1926,6 +1915,6 @@ RED.subflow = (function() { buildPropertiesList: buildPropertiesList, exportSubflowTemplateEnv: exportEnvList, - exportSubflowInstanceEnv: exportSubflowInstanceEnv, + exportSubflowInstanceEnv: exportSubflowInstanceEnv } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 04c904c8b..3f151a851 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -75,81 +75,6 @@ RED.workspaces = (function() { RED.sidebar.config.refresh(); } - var tabflowEditor; - - function buildProperties(container, workspace) { - var dialogForm = $('
').appendTo(container); - $('
'+ - ''+ - ''+ - '
').appendTo(dialogForm); - - var row = $('
'+ - ''+ - '
'+ - '
').appendTo(dialogForm); - tabflowEditor = RED.editor.createEditor({ - id: 'node-input-info', - mode: 'ace/mode/markdown', - value: "" - }); - - $('#node-info-input-info-expand').on("click", function(e) { - e.preventDefault(); - var value = tabflowEditor.getValue(); - RED.editor.editMarkdown({ - value: value, - width: "Infinity", - cursor: tabflowEditor.getCursorPosition(), - complete: function(v,cursor) { - tabflowEditor.setValue(v, -1); - tabflowEditor.gotoLine(cursor.row+1,cursor.column,false); - setTimeout(function() { - tabflowEditor.focus(); - },300); - } - }) - }); - - $('').prependTo(dialogForm); - dialogForm.on("submit", function(e) { e.preventDefault();}); - - $("#node-input-name").val(workspace.label); - RED.text.bidi.prepareInput($("#node-input-name")); - tabflowEditor.getSession().setValue(workspace.info || "", -1); - dialogForm.i18n(); - } - - function getNodeCredentials(type, id, done) { - var timeoutNotification; - var intialTimeout = setTimeout(function() { - timeoutNotification = RED.notify($('

').i18n(),{fixed: true}) - },800); - - $.ajax({ - url: "credentials/tab/" + id, - dataType: 'json', - success: function(data) { - if (timeoutNotification) { - timeoutNotification.close(); - timeoutNotification = null; - } - clearTimeout(intialTimeout); - done(data); - }, - error: function(jqXHR,status,error) { - if (timeoutNotification) { - timeoutNotification.close(); - timeoutNotification = null; - } - clearTimeout(intialTimeout); - RED.notify(RED._("editor.errors.credentialLoadFailed"),"error") - done(null); - }, - timeout: 30000, - }); - } - function showEditWorkspaceDialog(id) { var workspace = RED.nodes.workspace(id); if (!workspace) { @@ -157,207 +82,9 @@ RED.workspaces = (function() { if (subflow) { RED.editor.editSubflow(subflow); } - return; + } else { + RED.editor.editFlow(workspace); } - RED.view.state(RED.state.EDITING); - var trayOptions = { - title: RED._("workspace.editFlow",{name:RED.utils.sanitize(workspace.label)}), - buttons: [ - { - id: "node-dialog-delete", - class: 'leftButton'+((workspaceTabCount === 1)?" disabled":""), - text: RED._("common.label.delete"), //'', - click: function() { - deleteWorkspace(workspace); - RED.tray.close(); - } - }, - { - id: "node-dialog-cancel", - text: RED._("common.label.cancel"), - click: function() { - RED.tray.close(); - } - }, - { - id: "node-dialog-ok", - class: "primary", - text: RED._("common.label.done"), - click: function() { - var label = $( "#node-input-name" ).val(); - var changed = false; - var changes = {}; - if (workspace.label != label) { - changes.label = workspace.label; - changed = true; - workspace.label = label; - workspace_tabs.renameTab(workspace.id,label); - } - var disabled = $("#node-input-disabled").prop("checked"); - if (workspace.disabled !== disabled) { - changes.disabled = workspace.disabled; - changed = true; - workspace.disabled = disabled; - } - var info = tabflowEditor.getValue(); - if (workspace.info !== info) { - changes.info = workspace.info; - changed = true; - workspace.info = info; - } - $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled); - $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); - - var old_env = workspace.env; - var new_env = RED.subflow.exportSubflowInstanceEnv(workspace); - if (new_env && (new_env.length > 0)) { - new_env.forEach(function(prop) { - if (prop.type === "cred") { - workspace.credentials = workspace.credentials || {_:{}}; - workspace.credentials[prop.name] = prop.value; - workspace.credentials['has_'+prop.name] = (prop.value !== ""); - if (prop.value !== '__PWRD__') { - changed = true; - } - delete prop.value; - } - }); - } - if (!isSameObj(old_env, new_env)) { - workspace.env = new_env; - changes.env = old_env; - changed = true; - } - - if (changed) { - var historyEvent = { - t: "edit", - changes:changes, - node: workspace, - dirty: RED.nodes.dirty() - } - workspace.changed = true; - RED.history.push(historyEvent); - RED.nodes.dirty(true); - RED.sidebar.config.refresh(); - if (changes.hasOwnProperty('disabled')) { - RED.nodes.eachNode(function(n) { - if (n.z === workspace.id) { - n.dirty = true; - } - }); - RED.view.redraw(); - } - RED.events.emit("flows:change",workspace); - } - RED.tray.close(); - } - } - ], - resize: function(dimensions) { - var height = dimensions.height; - - var rows = $("#dialog-form>div:not(.node-text-editor-row)"); - var editorRow = $("#dialog-form>div.node-text-editor-row"); - for (var i=0; i
').appendTo(trayFooter) - - var editorTabEl = $('').appendTo(trayBody); - var editorContent = $('
').appendTo(trayBody); - - var finishedBuilding = false; - - var editorTabs = RED.tabs.create({ - element:editorTabEl, - onchange:function(tab) { - editorContent.children().hide(); - if (tab.onchange) { - tab.onchange.call(tab); - } - tab.content.show(); - if (finishedBuilding) { - RED.tray.resize(); - } - }, - collapsible: true, - menu: false - }); - - var nodePropertiesTab = { - id: "editor-tab-properties", - label: RED._("editor-tab.properties"), - name: RED._("editor-tab.properties"), - content: $('
', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), - iconClass: "fa fa-cog" - }; - buildProperties(nodePropertiesTab.content, workspace); - editorTabs.addTab(nodePropertiesTab); - - var tabPropertiesTab = { - id: "editor-tab-envProperties", - label: RED._("editor-tab.envProperties"), - name: RED._("editor-tab.envProperties"), - content: $('
', { - id: "editor-tab-envProperties-content", - class: "red-ui-tray-content" - }).appendTo(editorContent).hide(), - iconClass: "fa fa-list", - }; - - function cb() { - RED.subflow.buildPropertiesForm(workspace); - editorTabs.addTab(tabPropertiesTab); - - if (!workspace.hasOwnProperty("disabled")) { - workspace.disabled = false; - } - - $('').prop("checked",workspace.disabled).appendTo(trayFooterLeft).toggleButton({ - enabledIcon: "fa-circle-thin", - disabledIcon: "fa-ban", - invertState: true - }) - finishedBuilding = true; - RED.tray.resize(); - } - - if (workspace.credentials) { - cb(); - } - else { - getNodeCredentials("tab", workspace.id, function(data) { - if (data) { - workspace.credentials = data; - workspace.credentials._ = $.extend(true,{},data); - } - cb(); - }); - } - - }, - close: function() { - if (RED.view.state() != RED.state.IMPORT_DRAGGING) { - RED.view.state(RED.state.DEFAULT); - } - var selection = RED.view.selection(); - if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) { - RED.sidebar.info.refresh(workspace); - } - tabflowEditor.destroy(); - } - } - RED.tray.show(trayOptions); } @@ -566,7 +293,6 @@ RED.workspaces = (function() { } } - function removeWorkspace(ws) { if (!ws) { deleteWorkspace(RED.nodes.workspace(activeWorkspace)); @@ -595,7 +321,10 @@ RED.workspaces = (function() { return { init: init, add: addWorkspace, + // remove: remove workspace without editor history etc remove: removeWorkspace, + // delete: remove workspace and update editor history + delete: deleteWorkspace, order: setWorkspaceOrder, edit: editWorkspace, contains: function(id) {