Compare commits

..

17 Commits

Author SHA1 Message Date
Nick O'Leary
6abe66934e Prevent opening locked node's edit dialog 2023-02-23 23:36:51 +00:00
Nick O'Leary
196a9ae43a Merge pull request #4065 from node-red/node-help-link
Add link to node help in node edit dialog footer
2023-02-21 14:25:40 +00:00
Nick O'Leary
196773d6ba Merge branch 'master' into dev 2023-02-20 18:17:16 +00:00
Nick O'Leary
d8dbe68425 Merge pull request #4064 from node-red/reverse-tab-scroll
Reverse direction of tab scroll to expected direction
2023-02-20 18:15:04 +00:00
Nick O'Leary
dec3164fdd Reverse direction of tab scroll to expected direction 2023-02-20 17:30:16 +00:00
Nick O'Leary
c2772e5038 Merge pull request #4051 from sonntam/feature-editor-multipleToNode
Added editor feature for connecting multiple nodes to single node
2023-02-20 17:00:14 +00:00
Nick O'Leary
06e35baeaa Merge pull request #4060 from node-red-hitachi/fix-empty-group-on-outliner
Fix to add empty marker to empty group
2023-02-20 16:51:17 +00:00
Nick O'Leary
c9d72d7a1d Merge pull request #4056 from kazuhitoyokoi/master-validator
Add validators for complete and link call nodes
2023-02-20 16:50:01 +00:00
Nick O'Leary
62e9572070 Merge pull request #4048 from kazuhitoyokoi/master-notification
Show scrollbar in notification dialog only when needed
2023-02-20 16:45:10 +00:00
Nick O'Leary
fea68c8acc Merge pull request #4050 from kazuhitoyokoi/master-addtooltip
Add tooltip for show/hide button on info sidebar
2023-02-20 16:44:38 +00:00
Hiroyasu Nishiyama
929f0e90ac add empty marker to group 2023-02-14 20:49:43 +09:00
Kazuhito Yokoi
81331e68d2 Add validator for link call node 2023-02-10 18:55:10 +09:00
Kazuhito Yokoi
4fda59a585 Add validator for complete node 2023-02-10 16:49:29 +09:00
sonntam
4c7c855f2c fix code style 2023-02-04 21:31:49 +01:00
Marcus Sonntag
8740ec5570 Added editor feature for connecting multiple nodes to single node 2023-02-04 21:03:30 +01:00
Kazuhito Yokoi
ede3ac4282 Add tooltip for show/hide button on info sidebar 2023-02-04 20:55:34 +09:00
Kazuhito Yokoi
16f8b78b39 Show scrollbar in notification dialog only when needed 2023-02-04 11:16:28 +09:00
18 changed files with 154 additions and 51 deletions

View File

@@ -699,7 +699,10 @@
"triggerAction": "Trigger action",
"find": "Find in workspace",
"copyItemUrl": "Copy item url",
"copyURL2Clipboard": "Copied url to clipboard"
"copyURL2Clipboard": "Copied url to clipboard",
"showFlow": "Show",
"hideFlow": "Hide",
"find": "Find in workspace"
},
"help": {
"name": "Help",

View File

@@ -699,7 +699,10 @@
"triggerAction": "アクションを実行",
"find": "ワークスペース内を検索",
"copyItemUrl": "要素のURLをコピー",
"copyURL2Clipboard": "URLをクリップボードにコピーしました"
"copyURL2Clipboard": "URLをクリップボードにコピーしました",
"showFlow": "表示",
"hideFlow": "非表示",
"find": "ワークスペース内を検索"
},
"help": {
"name": "ヘルプ",

View File

@@ -503,7 +503,7 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
$("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
if (RED.workspaces.active() === 0 || RED.workspaces.isActiveLocked()) {
if (RED.workspaces.active() === 0 || RED.workspaces.isLocked()) {
$("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected");
$("#red-ui-clipboard-dialog-import-opt-new").addClass("selected");
} else {
@@ -1278,7 +1278,7 @@ RED.clipboard = (function() {
RED.keyboard.add("#red-ui-drop-target", "escape" ,hideDropTarget);
$('#red-ui-workspace-chart').on("dragenter",function(event) {
if (!RED.workspaces.isActiveLocked() && (
if (!RED.workspaces.isLocked() && (
$.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1)) {
$("#red-ui-drop-target").css({display:'table'}).focus();
@@ -1288,7 +1288,7 @@ RED.clipboard = (function() {
$('#red-ui-drop-target').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1 ||
RED.workspaces.isActiveLocked()) {
RED.workspaces.isLocked()) {
event.preventDefault();
}
})
@@ -1296,7 +1296,7 @@ RED.clipboard = (function() {
hideDropTarget();
})
.on("drop",function(event) {
if (!RED.workspaces.isActiveLocked()) {
if (!RED.workspaces.isLocked()) {
try {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");

View File

@@ -183,7 +183,7 @@ RED.tabs = (function() {
// Assume this is wheel event which might not trigger
// the scroll event, so do things manually
var sl = scrollContainer.scrollLeft();
sl -= evt.originalEvent.deltaY;
sl += evt.originalEvent.deltaY;
scrollContainer.scrollLeft(sl);
}
})

View File

@@ -28,7 +28,7 @@ RED.contextMenu = (function () {
const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
const canDelete = hasSelection || hasLinks
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
const canEdit = !RED.workspaces.isActiveLocked()
const canEdit = !RED.workspaces.isLocked()
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0

View File

@@ -860,6 +860,7 @@ RED.editor = (function() {
function showEditDialog(node, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
if (node.z && RED.workspaces.isLocked(node.z)) { return }
var editing_node = node;
var removeInfoEditorOnClose = false;
var skipInfoRefreshOnClose = false;
@@ -1155,6 +1156,8 @@ RED.editor = (function() {
var editing_config_node = RED.nodes.node(id);
var activeEditPanes = [];
if (editing_config_node && editing_config_node.z && RED.workspaces.isLocked(editing_config_node.z)) { return }
var configNodeScope = ""; // default to global
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
if (activeSubflow) {
@@ -1708,6 +1711,7 @@ RED.editor = (function() {
function showEditGroupDialog(group, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
if (group.z && RED.workspaces.isLocked(group.z)) { return }
var editing_node = group;
editStack.push(group);
RED.view.state(RED.state.EDITING);

View File

@@ -188,7 +188,7 @@ RED.group = (function() {
var activateMerge = false;
var activateRemove = false;
var singleGroupSelected = false;
var locked = RED.workspaces.isActiveLocked()
var locked = RED.workspaces.isLocked()
if (activateGroup) {
singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group';
@@ -266,7 +266,7 @@ RED.group = (function() {
}
}
function pasteGroupStyle() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
if (groupStyleClipboard) {
var selection = RED.view.selection();
@@ -301,7 +301,7 @@ RED.group = (function() {
}
function groupSelection() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -320,7 +320,7 @@ RED.group = (function() {
}
}
function ungroupSelection() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -344,7 +344,7 @@ RED.group = (function() {
}
function ungroup(g) {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
var nodes = [];
var parentGroup = RED.nodes.group(g.g);
g.nodes.forEach(function(n) {
@@ -371,7 +371,7 @@ RED.group = (function() {
}
function mergeSelection() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -441,7 +441,7 @@ RED.group = (function() {
}
function removeSelection() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -469,7 +469,7 @@ RED.group = (function() {
}
}
function createGroup(nodes) {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (nodes.length === 0) {
return;
}
@@ -575,7 +575,7 @@ RED.group = (function() {
markDirty(group);
}
function removeFromGroup(group, nodes, reparent) {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (!Array.isArray(nodes)) {
nodes = [nodes];
}

View File

@@ -573,7 +573,7 @@ RED.subflow = (function() {
}
});
RED.events.on("view:selection-changed",function(selection) {
if (!selection.nodes || RED.workspaces.isActiveLocked()) {
if (!selection.nodes || RED.workspaces.isLocked()) {
RED.menu.setDisabled("menu-item-subflow-convert",true);
} else {
RED.menu.setDisabled("menu-item-subflow-convert",false);
@@ -636,7 +636,7 @@ RED.subflow = (function() {
}
function convertToSubflow() {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();

View File

@@ -135,6 +135,10 @@ RED.sidebar.info.outliner = (function() {
RED.workspaces.show(n.id, null, true);
}
});
RED.popover.tooltip(toggleVisibleButton, function () {
var isHidden = !div.hasClass("red-ui-info-outline-item-hidden");
return RED._("sidebar.info." + (isHidden ? "hideFlow" : "showFlow"));
});
}
if (n.type !== 'subflow') {
var toggleButton = $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) {
@@ -633,6 +637,9 @@ RED.sidebar.info.outliner = (function() {
objects[n.id].children = missingParents[n.id];
delete missingParents[n.id]
}
if (objects[n.id].children.length === 0) {
objects[n.id].children.push(getEmptyItem(n.id));
}
}
var parent = n.g||n.z||"__global__";

View File

@@ -39,7 +39,7 @@ RED.view.tools = (function() {
}
function alignToGrid() {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
@@ -90,7 +90,7 @@ RED.view.tools = (function() {
}
function moveSelection(dx,dy) {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
if (moving_set === null) {
@@ -159,7 +159,7 @@ RED.view.tools = (function() {
}
function setSelectedNodeLabelState(labelShown) {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
@@ -448,7 +448,7 @@ RED.view.tools = (function() {
}
function alignSelectionToEdge(direction) {
// if (RED.workspaces.isActiveLocked()) {
// if (RED.workspaces.isLocked()) {
// return
// }
var selection = RED.view.selection();
@@ -552,7 +552,7 @@ RED.view.tools = (function() {
}
function distributeSelection(direction) {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
@@ -713,7 +713,7 @@ RED.view.tools = (function() {
}
function reorderSelection(dir) {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
@@ -752,7 +752,7 @@ RED.view.tools = (function() {
}
function wireSeriesOfNodes() {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
@@ -795,7 +795,7 @@ RED.view.tools = (function() {
}
function wireNodeToMultiple() {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
@@ -840,12 +840,70 @@ RED.view.tools = (function() {
}
}
function wireMultipleToNode() {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
var targetNode = selection.nodes[selection.nodes.length - 1];
if (targetNode.inputs === 0) {
return;
}
var i = 0;
var newLinks = [];
for (i = 0; i < selection.nodes.length - 1; i++) {
var sourceNode = selection.nodes[i];
if (sourceNode.outputs > 0) {
// Wire the first output to the target that has no link to the target yet.
// This allows for connecting all combinations of inputs/outputs.
// The user may then delete links quickly that aren't needed.
var sourceConnectedOutports = RED.nodes.filterLinks({
source: sourceNode,
target: targetNode
});
// Get outport indices that have no link yet
var sourceOutportIndices = Array.from({ length: sourceNode.outputs }, (_, i) => i);
var sourceConnectedOutportIndices = sourceConnectedOutports.map( x => x.sourcePort );
var sourceFreeOutportIndices = sourceOutportIndices.filter(x => !sourceConnectedOutportIndices.includes(x));
// Does an unconnected source port exist?
if (sourceFreeOutportIndices.length == 0) {
continue;
}
// Connect the first free outport to the target
var newLink = {
source: sourceNode,
target: targetNode,
sourcePort: sourceFreeOutportIndices[0]
}
RED.nodes.addLink(newLink);
newLinks.push(newLink);
}
}
if (newLinks.length > 0) {
RED.history.push({
t: 'add',
links: newLinks,
dirty: RED.nodes.dirty()
})
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
/**
* Splits selected wires and re-joins them with link-out+link-in
* @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
*/
function splitWiresWithLinkNodes(wires) {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
@@ -1016,7 +1074,7 @@ 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()) {
if (RED.workspaces.isLocked()) {
return
}
options = Object.assign({
@@ -1089,7 +1147,7 @@ RED.view.tools = (function() {
}
function addJunctionsToWires(wires) {
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
return
}
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
@@ -1307,6 +1365,7 @@ RED.view.tools = (function() {
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
RED.actions.add("core:wire-multiple-to-node", function() { wireMultipleToNode() })
RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });

View File

@@ -671,7 +671,7 @@ 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()) {
if (RED.workspaces.isLocked()) {
return
}
importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'});
@@ -2549,7 +2549,7 @@ RED.view = (function() {
}
function editSelection() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (movingSet.length() > 0) {
var node = movingSet.get(0).n;
if (node.type === "subflow") {
@@ -2883,7 +2883,7 @@ RED.view = (function() {
function detachSelectedNodes() {
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
var selection = RED.view.selection();
if (selection.nodes) {
const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes);
@@ -3465,7 +3465,7 @@ RED.view = (function() {
}
if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < dblClickInterval) {
mouse_mode = RED.state.DEFAULT;
if (RED.workspaces.isActiveLocked()) {
if (RED.workspaces.isLocked()) {
clickElapsed = 0;
d3.event.stopPropagation();
return
@@ -4009,7 +4009,7 @@ RED.view = (function() {
if (RED.view.DEBUG) {
console.warn("groupMouseUp", { mouse_mode, event: d3.event });
}
if (RED.workspaces.isActiveLocked()) { return }
if (RED.workspaces.isLocked()) { return }
if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) {
mouse_mode = RED.state.DEFAULT;
RED.editor.editGroup(g);
@@ -4233,7 +4233,7 @@ RED.view = (function() {
function showTouchMenu(obj,pos) {
var mdn = mousedown_node;
var options = [];
const isActiveLocked = RED.workspaces.isActiveLocked()
const isActiveLocked = RED.workspaces.isLocked()
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();}});

View File

@@ -160,7 +160,7 @@ RED.workspaces = (function() {
}
});
let isCurrentLocked = RED.workspaces.isActiveLocked()
let isCurrentLocked = RED.workspaces.isLocked()
if (tab) {
isCurrentLocked = tab.locked
}
@@ -833,8 +833,9 @@ RED.workspaces = (function() {
active: function() {
return activeWorkspace
},
isActiveLocked: function() {
var ws = RED.nodes.workspace(activeWorkspace) || RED.nodes.subflow(activeWorkspace)
isLocked: function(id) {
id = id || activeWorkspace
var ws = RED.nodes.workspace(id) || RED.nodes.subflow(id)
return ws && ws.locked
},
selection: function() {

View File

@@ -32,7 +32,7 @@
color: var(--red-ui-primary-text-color);
border: 1px solid var(--red-ui-notification-border-default);
border-left-width: 16px;
overflow: scroll;
overflow: auto;
max-height: 80vh;
.ui-dialog-buttonset {
margin-top: 20px;

View File

@@ -18,7 +18,16 @@
color:"#c0edc0",
defaults: {
name: {value:""},
scope: {value:[], type:"*[]"},
scope: {
value: [],
type: "*[]",
validate: function (v, opt) {
if (v.length > 0) {
return true;
}
return RED._("node-red:complete.errors.scopeUndefined");
}
},
uncaught: {value:false}
},
inputs:0,

View File

@@ -1,4 +1,3 @@
<script type="text/html" data-template-name="link in">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@@ -272,7 +271,17 @@
color:"#ddd",//"#87D8CF",
defaults: {
name: { value: "" },
links: { value: [], type:"link in[]" },
links: {
value: [],
type: "link in[]",
validate: function (v, opt) {
if ((this.linkType === "static" && v.length > 0)
|| this.linkType === "dynamic") {
return true;
}
return RED._("node-red:link.errors.linkUndefined");
}
},
linkType: { value:"static" },
timeout: {
value: "30",

View File

@@ -164,10 +164,10 @@ module.exports = function(RED) {
if (returnNode && returnNode.returnLinkMessage) {
returnNode.returnLinkMessage(messageEvent.id, msg);
} else {
node.warn(RED._("link.error.missingReturn"))
node.warn(RED._("link.errors.missingReturn"));
}
} else {
node.warn(RED._("link.error.missingReturn"))
node.warn(RED._("link.errors.missingReturn"));
}
done();
} else if (mode === "link") {

View File

@@ -119,7 +119,10 @@
}
},
"complete": {
"completeNodes": "complete: __number__"
"completeNodes": "complete: __number__",
"errors": {
"scopeUndefined": "scope undefined"
}
},
"debug": {
"output": "Output",
@@ -181,8 +184,9 @@
"staticLinkCall": "Fixed target",
"dynamicLinkCall": "Dynamic target (msg.target)",
"dynamicLinkLabel": "Dynamic",
"error": {
"missingReturn": "Missing return node information"
"errors": {
"missingReturn": "Missing return node information",
"linkUndefined": "link undefined"
}
},
"tls": {

View File

@@ -119,7 +119,10 @@
}
},
"complete": {
"completeNodes": "complete: __number__"
"completeNodes": "complete: __number__",
"errors": {
"scopeUndefined": "スコープが未定義"
}
},
"debug": {
"output": "対象",
@@ -181,8 +184,9 @@
"staticLinkCall": "対象を固定で指定",
"dynamicLinkCall": "対象を動的に指定 (msg.target)",
"dynamicLinkLabel": "動的",
"error": {
"missingReturn": "返却するノードの情報が存在しません"
"errors": {
"missingReturn": "返却するノードの情報が存在しません",
"linkUndefined": "リンクが未定義"
}
},
"tls": {