From f12d36b5ede4898fa8d3fc8a89da79870b6d01f9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Nov 2022 10:48:48 +0000 Subject: [PATCH] Locking flows fixes and context menu options --- .../editor-client/locales/en-US/editor.json | 2 + .../@node-red/editor-client/src/js/history.js | 6 +- .../@node-red/editor-client/src/js/nodes.js | 107 +++++++++--- .../editor-client/src/js/ui/clipboard.js | 50 +++--- .../editor-client/src/js/ui/contextMenu.js | 34 ++-- .../editor-client/src/js/ui/editor.js | 6 - .../src/js/ui/editors/panes/flowProperties.js | 2 - .../editor-client/src/js/ui/group.js | 22 ++- .../editor-client/src/js/ui/palette.js | 164 +++++++++--------- .../editor-client/src/js/ui/subflow.js | 5 +- .../editor-client/src/js/ui/tab-config.js | 20 ++- .../src/js/ui/tab-info-outliner.js | 3 +- .../editor-client/src/js/ui/view-tools.js | 47 ++++- .../@node-red/editor-client/src/js/ui/view.js | 138 ++++++++++----- .../editor-client/src/js/ui/workspaces.js | 43 ++++- .../editor-client/src/sass/flow.scss | 11 +- .../editor-client/src/sass/tab-info.scss | 10 +- 17 files changed, 447 insertions(+), 223 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 44d370aac..e74dbdbaa 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -71,6 +71,8 @@ "selectNodes": "Click nodes to select", "enableFlow": "Enable flow", "disableFlow": "Disable flow", + "lockFlow": "Lock flow", + "unlockFlow": "Unlock flow", "moveToStart": "Move flow to start", "moveToEnd": "Move flow to end" }, diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index b23071239..977ecb187 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -14,7 +14,7 @@ * limitations under the License. **/ -/** +/** * An API for undo / redo history buffer * @namespace RED.history */ @@ -434,7 +434,9 @@ RED.history = (function() { if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('disabled')) { $("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!ev.node.disabled); - $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!ev.node.disabled); + } + if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('locked')) { + $("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!ev.node.locked); } if (ev.subflow) { inverseEv.subflow = {}; diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index adbf86741..715ae9c49 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -19,7 +19,6 @@ * @namespace RED.nodes */ RED.nodes = (function() { - var PORT_TYPE_INPUT = 1; var PORT_TYPE_OUTPUT = 0; @@ -576,8 +575,41 @@ RED.nodes = (function() { } } + const nodeProxyHandler = { + get(node, prop) { + if (prop === '__isProxy__') { + return true + } else if (prop == '__node__') { + return node + } + return node[prop] + }, + set(node, prop, value) { + if (node.z && (RED.nodes.workspace(node.z)?.locked || RED.nodes.subflow(node.z)?.locked)) { + if ( + node._def.defaults[prop] || + prop === 'z' || + prop === 'l' || + prop === 'd' || + (prop === 'changed' && !!node.changed !== !!value) || + ((prop === 'x' || prop === 'y') && !node.resize && node.type !== 'group') + ) { + throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`) + } + } + node[prop] = value; + return true + } + } function addNode(n) { + let newNode + if (!n.__isProxy__) { + newNode = new Proxy(n, nodeProxyHandler) + } else { + newNode = n + } + if (n.type.indexOf("subflow") !== 0) { n["_"] = n._def._; } else { @@ -601,12 +633,13 @@ RED.nodes = (function() { }); n.i = nextId+1; } - allNodes.addNode(n); + allNodes.addNode(newNode); if (!nodeLinks[n.id]) { nodeLinks[n.id] = {in:[],out:[]}; } } - RED.events.emit('nodes:add',n); + RED.events.emit('nodes:add',newNode); + return newNode } function addLink(l) { if (nodeLinks[l.source.id]) { @@ -1335,7 +1368,6 @@ RED.nodes = (function() { } else { nodeSet = [sf]; } - console.log(nodeSet); return createExportableNodeSet(nodeSet); } /** @@ -2322,19 +2354,6 @@ RED.nodes = (function() { if (n.g && !new_group_set.has(n.g)) { delete n.g; } - n.nodes = n.nodes.map(function(id) { - return node_map[id]; - }) - // Just in case the group references a node that doesn't exist for some reason - n.nodes = n.nodes.filter(function(v) { - if (v) { - // Repair any nodes that have forgotten they are in this group - if (v.g !== n.id) { - v.g = n.id; - } - } - return !!v - }); if (!n.g) { groupDepthMap[n.id] = 0; } @@ -2357,21 +2376,22 @@ RED.nodes = (function() { return groupDepthMap[A.id] - groupDepthMap[B.id]; }); for (i=0;i { + const mappedNode = node_map[id] + if (!mappedNode) { + return null + } + if (mappedNode.__isProxy__) { + return mappedNode + } else { + return node_map[mappedNode.id] + } + } + // Update groups to reference proxy node objects + for (i=0;i 1 const canDelete = hasSelection || hasLinks const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group' - + const canEdit = !RED.workspaces.isActiveLocked() const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g + const offset = $("#red-ui-workspace-chart").offset() let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft() @@ -55,12 +56,13 @@ RED.contextMenu = (function () { splice: isSingleLink ? selection.links[0] : undefined, // spliceMultiple: isMultipleLinks }) - } + }, + disabled: !canEdit }, (hasLinks) ? { // has least 1 wire selected label: RED._("contextMenu.junction"), onselect: 'core:split-wires-with-junctions', - disabled: !hasLinks + disabled: !canEdit || !hasLinks } : { label: RED._("contextMenu.junction"), onselect: function () { @@ -86,41 +88,39 @@ RED.contextMenu = (function () { RED.nodes.dirty(true); RED.view.select({nodes: [nn] }); RED.view.redraw(true) - } + }, + disabled: !canEdit }, { label: RED._("contextMenu.linkNodes"), onselect: 'core:split-wire-with-link-nodes', - disabled: !hasLinks + disabled: !canEdit || !hasLinks } ] - } - ) - - menuItems.push( + }, null, { onselect: 'core:undo', disabled: RED.history.list().length === 0 }, { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 }, null, - { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection }, + { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection }, { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection }, - { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() }, - { onselect: 'core:delete-selection', disabled: !canDelete }, + { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() }, + { onselect: 'core:delete-selection', disabled: !canEdit || !canDelete }, { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }, - { onselect: 'core:select-all-nodes' } + { onselect: 'core:select-all-nodes' }, ) - if (hasSelection) { + if (hasSelection && canEdit) { menuItems.push( null, isGroup ? - { onselect: 'core:ungroup-selection', disabled: !isGroup } - : { onselect: 'core:group-selection', disabled: !hasSelection } + { onselect: 'core:ungroup-selection', disabled: !canEdit || !isGroup } + : { onselect: 'core:group-selection', disabled: !canEdit || !hasSelection } ) - if (canRemoveFromGroup) { + if (canRemoveFromGroup && canEdit) { menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") }) } 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 0a644ba42..29202a0ab 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 @@ -1847,9 +1847,6 @@ RED.editor = (function() { workspace.disabled = disabled; $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled); - if (workspace.id === RED.workspaces.active()) { - $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); - } } var locked = $("#node-input-locked").prop("checked"); @@ -1858,9 +1855,6 @@ RED.editor = (function() { editState.changed = true; workspace.locked = locked; $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked); - // if (workspace.id === RED.workspaces.active()) { - // $("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!workspace.locked); - // } } if (editState.changed) { var historyEvent = { 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 index 2db4d0c85..214335f1b 100644 --- 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 @@ -52,8 +52,6 @@ node.info = info; } $("#red-ui-tab-"+(node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!node.disabled); - $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!node.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 add6da6c9..15eda31d5 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 @@ -185,6 +185,8 @@ RED.group = (function() { var activateMerge = false; var activateRemove = false; var singleGroupSelected = false; + var locked = RED.workspaces.isActiveLocked() + if (activateGroup) { singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group'; selection.nodes.forEach(function (n) { @@ -199,12 +201,12 @@ RED.group = (function() { activateMerge = (selection.nodes.length > 1); } } - RED.menu.setDisabled("menu-item-group-group", !activateGroup); - RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup); - RED.menu.setDisabled("menu-item-group-merge", !activateMerge); - RED.menu.setDisabled("menu-item-group-remove", !activateRemove); + RED.menu.setDisabled("menu-item-group-group", locked || !activateGroup); + RED.menu.setDisabled("menu-item-group-ungroup", locked || !activateUngroup); + RED.menu.setDisabled("menu-item-group-merge", locked || !activateMerge); + RED.menu.setDisabled("menu-item-group-remove", locked || !activateRemove); RED.menu.setDisabled("menu-item-edit-copy-group-style", !singleGroupSelected); - RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup); + RED.menu.setDisabled("menu-item-edit-paste-group-style", locked || !activateUngroup); }); RED.actions.add("core:group-selection", function() { groupSelection() }) @@ -261,6 +263,7 @@ RED.group = (function() { } } function pasteGroupStyle() { + if (RED.workspaces.isActiveLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } if (groupStyleClipboard) { var selection = RED.view.selection(); @@ -295,6 +298,7 @@ RED.group = (function() { } function groupSelection() { + if (RED.workspaces.isActiveLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -313,6 +317,7 @@ RED.group = (function() { } } function ungroupSelection() { + if (RED.workspaces.isActiveLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -336,6 +341,7 @@ RED.group = (function() { } function ungroup(g) { + if (RED.workspaces.isActiveLocked()) { return } var nodes = []; var parentGroup = RED.nodes.group(g.g); g.nodes.forEach(function(n) { @@ -362,6 +368,7 @@ RED.group = (function() { } function mergeSelection() { + if (RED.workspaces.isActiveLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -431,6 +438,7 @@ RED.group = (function() { } function removeSelection() { + if (RED.workspaces.isActiveLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -458,6 +466,7 @@ RED.group = (function() { } } function createGroup(nodes) { + if (RED.workspaces.isActiveLocked()) { return } if (nodes.length === 0) { return; } @@ -480,7 +489,7 @@ RED.group = (function() { } group.z = nodes[0].z; - RED.nodes.addGroup(group); + group = RED.nodes.addGroup(group); try { addToGroup(group,nodes); @@ -563,6 +572,7 @@ RED.group = (function() { markDirty(group); } function removeFromGroup(group, nodes, reparent) { + if (RED.workspaces.isActiveLocked()) { return } if (!Array.isArray(nodes)) { nodes = [nodes]; } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js index 9f20cc674..ceaa33775 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js @@ -282,6 +282,7 @@ RED.palette = (function() { var hoverGroup; var paletteWidth; var paletteTop; + var dropEnabled; $(d).draggable({ helper: 'clone', appendTo: '#red-ui-editor', @@ -289,6 +290,7 @@ RED.palette = (function() { revertDuration: 200, containment:'#red-ui-main-container', start: function() { + dropEnabled = !RED.nodes.workspace(RED.workspaces.active()).locked; paletteWidth = $("#red-ui-palette").width(); paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top; hoverGroup = null; @@ -299,96 +301,100 @@ RED.palette = (function() { RED.view.focus(); }, stop: function() { - d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false); - if (hoverGroup) { - document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered"); + if (dropEnabled) { + d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false); + if (hoverGroup) { + document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered"); + } + if (activeGroup) { + document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered"); + } + if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; } + if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; } } - if (activeGroup) { - document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered"); - } - if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; } - if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; } }, drag: function(e,ui) { var paletteNode = getPaletteNode(nt); ui.originalPosition.left = paletteNode.offset().left; - mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft(); - mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10; - if (!groupTimer) { - groupTimer = setTimeout(function() { - var mx = mouseX / RED.view.scale(); - var my = mouseY / RED.view.scale(); - var group = RED.view.getGroupAtPoint(mx,my); - if (group !== hoverGroup) { - if (hoverGroup) { - document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered"); - } - if (group) { - document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered"); - } - hoverGroup = group; - if (hoverGroup) { - $(ui.helper).data('group',hoverGroup); - } else { - $(ui.helper).removeData('group'); - } - } - groupTimer = null; - - },200) - } - if (def.inputs > 0 && def.outputs > 0) { - if (!spliceTimer) { - spliceTimer = setTimeout(function() { - var nodes = []; - var bestDistance = Infinity; - var bestLink = null; - if (chartSVG.getIntersectionList) { - var svgRect = chartSVG.createSVGRect(); - svgRect.x = mouseX; - svgRect.y = mouseY; - svgRect.width = 1; - svgRect.height = 1; - nodes = chartSVG.getIntersectionList(svgRect,chartSVG); - } else { - // Firefox doesn't do getIntersectionList and that - // makes us sad - nodes = RED.view.getLinksAtPoint(mouseX,mouseY); - } + if (dropEnabled) { + mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft(); + mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10; + if (!groupTimer) { + groupTimer = setTimeout(function() { var mx = mouseX / RED.view.scale(); var my = mouseY / RED.view.scale(); - for (var i=0;i 0 && def.outputs > 0) { + if (!spliceTimer) { + spliceTimer = setTimeout(function() { + var nodes = []; + var bestDistance = Infinity; + var bestLink = null; + if (chartSVG.getIntersectionList) { + var svgRect = chartSVG.createSVGRect(); + svgRect.x = mouseX; + svgRect.y = mouseY; + svgRect.width = 1; + svgRect.height = 1; + nodes = chartSVG.getIntersectionList(svgRect,chartSVG); + } else { + // Firefox doesn't do getIntersectionList and that + // makes us sad + nodes = RED.view.getLinksAtPoint(mouseX,mouseY); + } + var mx = mouseX / RED.view.scale(); + var my = mouseY / RED.view.scale(); + for (var i=0;i').appendTo(parent); var header = $('
').appendTo(container); + let lockIcon if (label) { + lockIcon = $('').appendTo(header) + lockIcon.toggle(!!isLocked) $('').text(label).appendTo(header); } else { $('').appendTo(header); @@ -62,6 +65,7 @@ RED.sidebar.config = (function() { var icon = header.find("i"); var result = { label: label, + lockIcon, list: category, size: function() { return result.list.find("li:not(.red-ui-palette-node-config-none)").length @@ -100,6 +104,9 @@ RED.sidebar.config = (function() { }); categories[name] = result; } else { + if (isLocked !== undefined && categories[name].lockIcon) { + categories[name].lockIcon.toggle(!!isLocked) + } if (categories[name].label !== label) { categories[name].list.parent().find('.red-ui-palette-node-config-label').text(label); categories[name].label = label; @@ -216,7 +223,7 @@ RED.sidebar.config = (function() { RED.nodes.eachWorkspace(function(ws) { validList[ws.id.replace(/\./g,"-")] = true; - getOrCreateCategory(ws.id,flowCategories,ws.label); + getOrCreateCategory(ws.id,flowCategories,ws.label, ws.locked); }) RED.nodes.eachSubflow(function(sf) { validList[sf.id.replace(/\./g,"-")] = true; @@ -274,6 +281,15 @@ RED.sidebar.config = (function() { changes: {}, dirty: RED.nodes.dirty() } + for (let i = 0; i < selectedNodes.length; i++) { + let node = RED.nodes.node(selectedNodes[i]) + if (node.z) { + let ws = RED.nodes.workspace(node.z) + if (ws && ws.locked) { + return + } + } + } selectedNodes.forEach(function(id) { var node = RED.nodes.node(id); try { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index 20bf627f0..d398cc2d0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -381,7 +381,7 @@ RED.sidebar.info.outliner = (function() { objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) objects[ws.id].element.toggleClass("red-ui-info-outline-item-locked", !!ws.locked) - // objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) + objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-locked", !!ws.locked) updateSearch(); } @@ -397,6 +397,7 @@ RED.sidebar.info.outliner = (function() { existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled) existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled) existingObject.element.toggleClass("red-ui-info-outline-item-locked", !!n.locked) + existingObject.treeList.container.toggleClass("red-ui-info-outline-item-locked", !!n.locked) updateSearch(); } function onFlowsReorder(order) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 1ed5791a6..27d45dcd3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -15,7 +15,7 @@ **/ RED.view.tools = (function() { - + 'use strict'; function selectConnected(type) { var selection = RED.view.selection(); var visited = new Set(); @@ -39,6 +39,9 @@ RED.view.tools = (function() { } function alignToGrid() { + if (RED.workspaces.isActiveLocked()) { + return + } var selection = RED.view.selection(); if (selection.nodes) { var changedNodes = []; @@ -87,6 +90,9 @@ RED.view.tools = (function() { } function moveSelection(dx,dy) { + if (RED.workspaces.isActiveLocked()) { + return + } if (moving_set === null) { moving_set = []; var selection = RED.view.selection(); @@ -153,6 +159,9 @@ RED.view.tools = (function() { } function setSelectedNodeLabelState(labelShown) { + if (RED.workspaces.isActiveLocked()) { + return + } var selection = RED.view.selection(); var historyEvents = []; var nodes = []; @@ -439,6 +448,9 @@ RED.view.tools = (function() { } function alignSelectionToEdge(direction) { + // if (RED.workspaces.isActiveLocked()) { + // return + // } var selection = RED.view.selection(); if (selection.nodes && selection.nodes.length > 1) { @@ -539,8 +551,10 @@ RED.view.tools = (function() { } } - function distributeSelection(direction) { + if (RED.workspaces.isActiveLocked()) { + return + } var selection = RED.view.selection(); if (selection.nodes && selection.nodes.length > 2) { @@ -699,6 +713,9 @@ RED.view.tools = (function() { } function reorderSelection(dir) { + if (RED.workspaces.isActiveLocked()) { + return + } var selection = RED.view.selection(); if (selection.nodes) { var nodesToMove = []; @@ -734,8 +751,10 @@ RED.view.tools = (function() { } } - function wireSeriesOfNodes() { + if (RED.workspaces.isActiveLocked()) { + return + } var selection = RED.view.selection(); if (selection.nodes) { if (selection.nodes.length > 1) { @@ -776,6 +795,9 @@ RED.view.tools = (function() { } function wireNodeToMultiple() { + if (RED.workspaces.isActiveLocked()) { + return + } var selection = RED.view.selection(); if (selection.nodes) { if (selection.nodes.length > 1) { @@ -823,6 +845,9 @@ RED.view.tools = (function() { * @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes. */ function splitWiresWithLinkNodes(wires) { + if (RED.workspaces.isActiveLocked()) { + return + } let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link)); if (!wiresToSplit) { return @@ -877,7 +902,6 @@ RED.view.tools = (function() { if(!nnLinkOut) { const nLinkOut = RED.view.createNode("link out"); //create link node nnLinkOut = nLinkOut.node; - nodeSrcMap[linkOutMapId] = nnLinkOut; let yOffset = 0; if(nSrc.outputs > 1) { @@ -892,7 +916,8 @@ RED.view.tools = (function() { updateNewNodePosXY(nSrc, nnLinkOut, false, RED.view.snapGrid, yOffset); } //add created node - RED.nodes.add(nnLinkOut); + nnLinkOut = RED.nodes.add(nnLinkOut); + nodeSrcMap[linkOutMapId] = nnLinkOut; RED.editor.validateNode(nnLinkOut); history.events.push(nLinkOut.historyEvent); //connect node to link node @@ -913,10 +938,10 @@ RED.view.tools = (function() { if(!nnLinkIn) { const nLinkIn = RED.view.createNode("link in"); //create link node nnLinkIn = nLinkIn.node; - nodeTrgMap[nTrg.id] = nnLinkIn; updateNewNodePosXY(nTrg, nnLinkIn, true, RED.view.snapGrid, 0); //add created node - RED.nodes.add(nnLinkIn); + nnLinkIn = RED.nodes.add(nnLinkIn); + nodeTrgMap[nTrg.id] = nnLinkIn; RED.editor.validateNode(nnLinkIn); history.events.push(nLinkIn.historyEvent); //connect node to link node @@ -991,6 +1016,9 @@ RED.view.tools = (function() { * @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory` */ function generateNodeNames(node, options) { + if (RED.workspaces.isActiveLocked()) { + return + } options = Object.assign({ renameBlank: true, renameClash: true, @@ -1061,6 +1089,9 @@ RED.view.tools = (function() { } function addJunctionsToWires(wires) { + if (RED.workspaces.isActiveLocked()) { + return + } let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link)); if (!wiresToSplit) { return @@ -1131,7 +1162,7 @@ RED.view.tools = (function() { var nodeGroups = new Set() - RED.nodes.addJunction(junction) + junction = RED.nodes.addJunction(junction) addedJunctions.push(junction) let newLink if (gid === links[0].source.id+":"+links[0].sourcePort) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 83215afe4..d783ce941 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -54,6 +54,7 @@ RED.view = (function() { var spliceTimer; var groupHoverTimer; + var activeFlowLocked = false; var activeSubflow = null; var activeNodes = []; var activeLinks = []; @@ -411,6 +412,17 @@ RED.view = (function() { activeSubflow = RED.nodes.subflow(event.workspace); + if (activeSubflow) { + activeFlowLocked = activeSubflow.locked + } else { + var activeWorkspace = RED.nodes.workspace(event.workspace) + if (activeWorkspace) { + activeFlowLocked = activeWorkspace.locked + } else { + activeFlowLocked = true + } + } + RED.menu.setDisabled("menu-item-workspace-edit", activeSubflow || event.workspace === 0); RED.menu.setDisabled("menu-item-workspace-delete",event.workspace === 0 || RED.workspaces.count() == 1 || activeSubflow); @@ -439,6 +451,15 @@ RED.view = (function() { redraw(); }); + RED.events.on("flows:change", function(workspace) { + if (workspace.id === RED.workspaces.active()) { + activeFlowLocked = !!workspace.locked + $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); + $("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!workspace.locked); + + } + }) + RED.statusBar.add({ id: "view-zoom-controls", align: "right", @@ -496,6 +517,9 @@ RED.view = (function() { chart.droppable({ accept:".red-ui-palette-node", drop: function( event, ui ) { + if (activeFlowLocked) { + return + } d3.event = event; var selected_tool = $(ui.draggable[0]).attr("data-palette-type"); var result = createNode(selected_tool); @@ -503,9 +527,7 @@ RED.view = (function() { return; } var historyEvent = result.historyEvent; - var nn = result.node; - - RED.nodes.add(nn); + var nn = RED.nodes.add(result.node); var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label"); if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) { @@ -632,6 +654,9 @@ RED.view = (function() { RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection); RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection(true);deleteSelection();}); RED.actions.add("core:paste-from-internal-clipboard",function(){ + if (RED.workspaces.isActiveLocked()) { + return + } importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'}); }); @@ -640,22 +665,27 @@ RED.view = (function() { RED.events.on("view:selection-changed", function(selection) { var hasSelection = (selection.nodes && selection.nodes.length > 0); var hasMultipleSelection = hasSelection && selection.nodes.length > 1; - RED.menu.setDisabled("menu-item-edit-cut",!hasSelection); - RED.menu.setDisabled("menu-item-edit-copy",!hasSelection); - RED.menu.setDisabled("menu-item-edit-select-connected",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-to-back",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-to-front",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-backwards",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-forwards",!hasSelection); + var hasLinkSelected = selection.links && selection.links.length > 0; + var canEdit = !activeFlowLocked && hasSelection + var canEditMultiple = !activeFlowLocked && hasMultipleSelection + RED.menu.setDisabled("menu-item-edit-cut", !canEdit); + RED.menu.setDisabled("menu-item-edit-copy", !hasSelection); + RED.menu.setDisabled("menu-item-edit-select-connected", !hasSelection); + RED.menu.setDisabled("menu-item-view-tools-move-to-back", !canEdit); + RED.menu.setDisabled("menu-item-view-tools-move-to-front", !canEdit); + RED.menu.setDisabled("menu-item-view-tools-move-backwards", !canEdit); + RED.menu.setDisabled("menu-item-view-tools-move-forwards", !canEdit); - RED.menu.setDisabled("menu-item-view-tools-align-left",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-center",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-right",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-top",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-middle",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-bottom",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally",!hasMultipleSelection); + RED.menu.setDisabled("menu-item-view-tools-align-left", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-align-center", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-align-right", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-align-top", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-align-middle", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-align-bottom", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally", !canEditMultiple); + RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally", !canEditMultiple); + + RED.menu.setDisabled("menu-item-edit-split-wire-with-links", activeFlowLocked || !hasLinkSelected); }) RED.actions.add("core:delete-selection",deleteSelection); @@ -1045,7 +1075,7 @@ RED.view = (function() { .attr("class", "nr-ui-view-lasso"); d3.event.preventDefault(); } - } else if (d3.event.altKey) { + } else if (d3.event.altKey && !activeFlowLocked) { //Alt [+shift] held - Begin slicing clearSelection(); mouse_mode = (d3.event.shiftKey) ? RED.state.SLICING_JUNCTION : RED.state.SLICING; @@ -1059,6 +1089,9 @@ RED.view = (function() { } function showQuickAddDialog(options) { + if (activeFlowLocked) { + return + } options = options || {}; var point = options.position || lastClickPosition; var spliceLink = options.splice; @@ -1238,6 +1271,11 @@ RED.view = (function() { if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) { nn.l = showLabel; } + if (nn.type === 'junction') { + nn = RED.nodes.addJunction(nn); + } else { + nn = RED.nodes.add(nn); + } if (quickAddLink) { var drag_line = quickAddLink; var src = null,dst,src_port; @@ -1340,11 +1378,7 @@ RED.view = (function() { } } } - if (nn.type === 'junction') { - RED.nodes.addJunction(nn); - } else { - RED.nodes.add(nn); - } + RED.editor.validateNode(nn); if (targetGroup) { @@ -1602,16 +1636,18 @@ RED.view = (function() { } var d = (mouse_offset[0]-mousePos[0])*(mouse_offset[0]-mousePos[0]) + (mouse_offset[1]-mousePos[1])*(mouse_offset[1]-mousePos[1]); if ((d > 3 && !dblClickPrimed) || (dblClickPrimed && d > 10)) { - mouse_mode = RED.state.MOVING_ACTIVE; clickElapsed = 0; - spliceActive = false; - if (movingSet.length() === 1) { - node = movingSet.get(0); - spliceActive = node.n.hasOwnProperty("_def") && - ((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) && - ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) && - RED.nodes.filterLinks({ source: node.n }).length === 0 && - RED.nodes.filterLinks({ target: node.n }).length === 0; + if (!activeFlowLocked) { + mouse_mode = RED.state.MOVING_ACTIVE; + spliceActive = false; + if (movingSet.length() === 1) { + node = movingSet.get(0); + spliceActive = node.n.hasOwnProperty("_def") && + ((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) && + ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) && + RED.nodes.filterLinks({ source: node.n }).length === 0 && + RED.nodes.filterLinks({ target: node.n }).length === 0; + } } } } else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) { @@ -2456,6 +2492,7 @@ RED.view = (function() { } function editSelection() { + if (RED.workspaces.isActiveLocked()) { return } if (movingSet.length() > 0) { var node = movingSet.get(0).n; if (node.type === "subflow") { @@ -2471,6 +2508,9 @@ RED.view = (function() { if (mouse_mode === RED.state.SELECTING_NODE) { return; } + if (activeFlowLocked) { + return + } if (portLabelHover) { portLabelHover.remove(); portLabelHover = null; @@ -2786,6 +2826,7 @@ RED.view = (function() { function detachSelectedNodes() { + if (RED.workspaces.isActiveLocked()) { return } var selection = RED.view.selection(); if (selection.nodes) { const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes); @@ -2927,7 +2968,7 @@ RED.view = (function() { mousedown_node = d; mousedown_port_type = portType; mousedown_port_index = portIndex || 0; - if (mouse_mode !== RED.state.QUICK_JOINING) { + if (mouse_mode !== RED.state.QUICK_JOINING && !activeFlowLocked) { mouse_mode = RED.state.JOINING; document.body.style.cursor = "crosshair"; if (evt.ctrlKey || evt.metaKey) { @@ -3367,6 +3408,11 @@ RED.view = (function() { } if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < dblClickInterval) { mouse_mode = RED.state.DEFAULT; + if (RED.workspaces.isActiveLocked()) { + clickElapsed = 0; + d3.event.stopPropagation(); + return + } if (d.type != "subflow") { if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) { RED.workspaces.show(d.type.substring(8)); @@ -3690,7 +3736,6 @@ RED.view = (function() { } // selectedLinks.clear(); if (d3.event.button != 2) { - mouse_mode = RED.state.MOVING; var mouse = d3.touches(this)[0]||d3.mouse(this); mouse[0] += d.x-d.w/2; mouse[1] += d.y-d.h/2; @@ -3883,6 +3928,7 @@ RED.view = (function() { if (RED.view.DEBUG) { console.warn("groupMouseUp", { mouse_mode, event: d3.event }); } + if (RED.workspaces.isActiveLocked()) { return } if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) { mouse_mode = RED.state.DEFAULT; RED.editor.editGroup(g); @@ -4053,7 +4099,7 @@ RED.view = (function() { function isButtonEnabled(d) { var buttonEnabled = true; var ws = RED.nodes.workspace(RED.workspaces.active()); - if (ws && !ws.disabled && !d.d) { + if (ws && !ws.disabled && !d.d && !ws.locked) { if (d._def.button.hasOwnProperty('enabled')) { if (typeof d._def.button.enabled === "function") { buttonEnabled = d._def.button.enabled.call(d); @@ -4076,7 +4122,7 @@ RED.view = (function() { } var activeWorkspace = RED.workspaces.active(); var ws = RED.nodes.workspace(activeWorkspace); - if (ws && !ws.disabled && !d.d) { + if (ws && !ws.disabled && !d.d && !ws.locked) { if (d._def.button.toggle) { d[d._def.button.toggle] = !d[d._def.button.toggle]; d.dirty = true; @@ -4091,7 +4137,7 @@ RED.view = (function() { if (d.dirty) { redraw(); } - } else { + } else if (!ws || !ws.locked){ if (activeSubflow) { RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.nodeActionDisabledSubflow")}),"warning"); } else { @@ -4106,14 +4152,15 @@ RED.view = (function() { function showTouchMenu(obj,pos) { var mdn = mousedown_node; var options = []; - options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}}); - options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection(true);deleteSelection();}}); - options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}}); - options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}}); - options.push({name:"edit",disabled:(movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}}); + const isActiveLocked = RED.workspaces.isActiveLocked() + options.push({name:"delete",disabled:(isActiveLocked || movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}}); + options.push({name:"cut",disabled:(isActiveLocked || movingSet.length()===0),onselect:function() {copySelection(true);deleteSelection();}}); + options.push({name:"copy",disabled:(isActiveLocked || movingSet.length()===0),onselect:function() {copySelection();}}); + options.push({name:"paste",disabled:(isActiveLocked || clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}}); + options.push({name:"edit",disabled:(isActiveLocked || movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}}); options.push({name:"select",onselect:function() {selectAll();}}); options.push({name:"undo",disabled:(RED.history.depth() === 0),onselect:function() {RED.history.pop();}}); - options.push({name:"add",onselect:function() { + options.push({name:"add",disabled:isActiveLocked, onselect:function() { chartPos = chart.offset(); showQuickAddDialog({ position:[pos[0]-chartPos.left+chart.scrollLeft(),pos[1]-chartPos.top+chart.scrollTop()], @@ -5811,6 +5858,9 @@ RED.view = (function() { if (mouse_mode === RED.state.SELECTING_NODE) { return; } + if (activeFlowLocked) { + return + } var workspaceSelection = RED.workspaces.selection(); var changed = false; if (workspaceSelection.length > 0) { 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 23171fb5a..79f361a39 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 @@ -103,6 +103,9 @@ RED.workspaces = (function() { if (workspaceTabCount === 1) { return; } + if (ws.locked) { + return + } var workspaceOrder = RED.nodes.getWorkspaceOrder(); ws._index = workspaceOrder.indexOf(ws.id); removeWorkspace(ws); @@ -123,7 +126,9 @@ RED.workspaces = (function() { RED.editor.editSubflow(subflow); } } else { - RED.editor.editFlow(workspace); + if (!workspace.locked) { + RED.editor.editFlow(workspace); + } } } @@ -148,6 +153,11 @@ RED.workspaces = (function() { let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active()) let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false + let isCurrentLocked = RED.workspaces.isActiveLocked() + if (tab) { + isCurrentLocked = tab.locked + } + var menuItems = [] if (isMenuButton) { menuItems.push({ @@ -188,14 +198,30 @@ RED.workspaces = (function() { shortcut: RED.keyboard.getShortcut("core:enable-flow"), onselect: function() { RED.actions.invoke("core:enable-flow", tab?tab.id:undefined) - } + }, + disabled: isCurrentLocked } : { label: RED._("workspace.disableFlow"), shortcut: RED.keyboard.getShortcut("core:disable-flow"), onselect: function() { RED.actions.invoke("core:disable-flow", tab?tab.id:undefined) + }, + disabled: isCurrentLocked + }, + isCurrentLocked? { + label: RED._("workspace.unlockFlow"), + shortcut: RED.keyboard.getShortcut("core:unlock-flow"), + onselect: function() { + RED.actions.invoke('core:unlock-flow', tab?tab.id:undefined) } - } + } : { + label: RED._("workspace.lockFlow"), + shortcut: RED.keyboard.getShortcut("core:lock-flow"), + onselect: function() { + RED.actions.invoke('core:lock-flow', tab?tab.id:undefined) + } + }, + null ) } const currentTabs = workspace_tabs.listTabs() @@ -239,6 +265,7 @@ RED.workspaces = (function() { } } ) + } menuItems.push( { @@ -264,6 +291,7 @@ RED.workspaces = (function() { null, { label: RED._("common.label.delete"), + disabled: isCurrentLocked, onselect: function() { if (tab.type === 'tab') { RED.workspaces.delete(tab) @@ -302,6 +330,7 @@ RED.workspaces = (function() { activeWorkspace = tab.id; window.location.hash = 'flow/'+tab.id; $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!tab.disabled); + $("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!tab.locked); } else { $("#red-ui-workspace-chart").hide(); activeWorkspace = 0; @@ -615,7 +644,7 @@ RED.workspaces = (function() { } function setWorkspaceState(id,disabled) { var workspace = RED.nodes.workspace(id||activeWorkspace); - if (!workspace) { + if (!workspace || workspace.locked) { return; } if (workspace.disabled !== disabled) { @@ -695,6 +724,8 @@ RED.workspaces = (function() { } function removeWorkspace(ws) { + if (ws.locked) { return } + if (!ws) { deleteWorkspace(RED.nodes.workspace(activeWorkspace)); } else { @@ -792,6 +823,10 @@ RED.workspaces = (function() { active: function() { return activeWorkspace }, + isActiveLocked: function() { + var ws = RED.nodes.workspace(activeWorkspace) || RED.nodes.subflow(activeWorkspace) + return ws && ws.locked + }, selection: function() { return workspace_tabs.selection(); }, diff --git a/packages/node_modules/@node-red/editor-client/src/sass/flow.scss b/packages/node_modules/@node-red/editor-client/src/sass/flow.scss index be8db6c93..3e5be0645 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/flow.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/flow.scss @@ -68,6 +68,9 @@ stroke: var(--red-ui-node-border); cursor: move; stroke-width: 1; + .red-ui-workspace-locked & { + cursor: pointer; + } } .red-ui-workspace-select-mode { g.red-ui-flow-node.red-ui-flow-node-hovered * { @@ -287,9 +290,11 @@ g.red-ui-flow-node-selected { text-anchor:start; } -.red-ui-flow-port-hovered { - stroke: var(--red-ui-port-selected-color); - fill: var(--red-ui-port-selected-color); +#red-ui-workspace:not(.red-ui-workspace-locked) { + .red-ui-flow-port-hovered { + stroke: var(--red-ui-port-selected-color); + fill: var(--red-ui-port-selected-color); + } } .red-ui-flow-subflow-port { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss index ed526b263..f6bf2473c 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-info.scss @@ -514,6 +514,14 @@ div.red-ui-info-table { display: none; } } + // If the parent is locked, do not show the display/action buttons when + // hovering in the outline + .red-ui-info-outline-item-locked .red-ui-info-outline-item & { + .red-ui-info-outline-item-control-disable, + .red-ui-info-outline-item-control-action { + display: none; + } + } button { margin-right: 3px } @@ -531,8 +539,6 @@ div.red-ui-info-table { } } - - .red-ui-icons { display: inline-block; width: 18px;