mirror of
https://github.com/node-red/node-red.git
synced 2025-12-27 07:31:07 +01:00
Support multiple suggestions at once
This commit is contained in:
@@ -1909,6 +1909,7 @@ RED.nodes = (function() {
|
||||
* - id:replace - import over the top of existing
|
||||
* - modules: map of module:version - hints for unknown nodes
|
||||
* - applyNodeDefaults - whether to apply default values to the imported nodes (default: false)
|
||||
* - eventContext - context to include in the `nodes:add` event
|
||||
*/
|
||||
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
|
||||
const defOpts = {
|
||||
@@ -1917,7 +1918,8 @@ RED.nodes = (function() {
|
||||
markChanged: false,
|
||||
reimport: false,
|
||||
importMap: {},
|
||||
applyNodeDefaults: false
|
||||
applyNodeDefaults: false,
|
||||
eventContext: null
|
||||
}
|
||||
options = Object.assign({}, defOpts, options)
|
||||
options.importMap = options.importMap || {}
|
||||
@@ -2793,7 +2795,7 @@ RED.nodes = (function() {
|
||||
|
||||
// Now the nodes have been fully updated, add them.
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
new_nodes[i] = addNode(new_nodes[i])
|
||||
new_nodes[i] = addNode(new_nodes[i], options.eventContext)
|
||||
node_map[new_nodes[i].id] = new_nodes[i]
|
||||
}
|
||||
|
||||
|
||||
@@ -302,23 +302,27 @@ RED.keyboard = (function() {
|
||||
return resolveKeyEvent(evt);
|
||||
}
|
||||
}
|
||||
d3.select(window).on("keydown",function() {
|
||||
d3.select(window).on("keydown", () => {
|
||||
handleEvent(d3.event)
|
||||
})
|
||||
|
||||
function handleEvent (evt) {
|
||||
if (!handlersActive) {
|
||||
return;
|
||||
}
|
||||
if (metaKeyCodes[d3.event.keyCode]) {
|
||||
if (metaKeyCodes[evt]) {
|
||||
return;
|
||||
}
|
||||
var handler = resolveKeyEvent(d3.event);
|
||||
var handler = resolveKeyEvent(evt);
|
||||
if (handler && handler.ondown) {
|
||||
if (typeof handler.ondown === "string") {
|
||||
RED.actions.invoke(handler.ondown);
|
||||
} else {
|
||||
handler.ondown();
|
||||
}
|
||||
d3.event.preventDefault();
|
||||
}
|
||||
});
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function addHandler(scope,key,modifiers,ondown) {
|
||||
var mod = modifiers;
|
||||
@@ -700,7 +704,8 @@ RED.keyboard = (function() {
|
||||
formatKey: formatKey,
|
||||
validateKey: validateKey,
|
||||
disable: disable,
|
||||
enable: enable
|
||||
enable: enable,
|
||||
handle: handleEvent
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -793,7 +793,11 @@ RED.view = (function() {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'});
|
||||
importNodes(clipboard, {
|
||||
generateIds: clipboardSource === 'copy',
|
||||
generateDefaultNames: clipboardSource === 'copy',
|
||||
eventContext: { source: 'clipboard' }
|
||||
});
|
||||
});
|
||||
|
||||
RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() })
|
||||
@@ -1746,6 +1750,7 @@ RED.view = (function() {
|
||||
}
|
||||
},
|
||||
suggest: function (suggestion) {
|
||||
// TypeSearch only provides one suggestion at a time
|
||||
if (suggestion?.nodes?.length > 0) {
|
||||
// Reposition the suggestion relative to the existing ghost node position
|
||||
const deltaX = (suggestion.nodes[0].x || 0) - point[0]
|
||||
@@ -5850,7 +5855,8 @@ RED.view = (function() {
|
||||
generateIds: false,
|
||||
generateDefaultNames: false,
|
||||
notify: true,
|
||||
applyNodeDefaults: false
|
||||
applyNodeDefaults: false,
|
||||
eventContext: null
|
||||
}
|
||||
const addNewFlow = options.addFlow
|
||||
const touchImport = options.touchImport;
|
||||
@@ -5940,7 +5946,8 @@ RED.view = (function() {
|
||||
importMap: options.importMap,
|
||||
markChanged: true,
|
||||
modules: modules,
|
||||
applyNodeDefaults: applyNodeDefaults
|
||||
applyNodeDefaults: applyNodeDefaults,
|
||||
eventContext: options.eventContext
|
||||
});
|
||||
if (importResult) {
|
||||
var new_nodes = importResult.nodes;
|
||||
@@ -6512,6 +6519,8 @@ RED.view = (function() {
|
||||
*/
|
||||
function setSuggestedFlow (suggestion) {
|
||||
$(window).off('keydown.suggestedFlow')
|
||||
$(window).off('mousedown.suggestedFlow');
|
||||
RED.keyboard.enable()
|
||||
if (!currentSuggestion && !suggestion) {
|
||||
// Avoid unnecessary redraws
|
||||
return
|
||||
@@ -6519,7 +6528,28 @@ RED.view = (function() {
|
||||
// Clear up any existing suggestion state
|
||||
clearSuggestedFlow()
|
||||
currentSuggestion = suggestion
|
||||
if (suggestion?.nodes?.length > 0) {
|
||||
if (suggestion && suggestion.nodes) {
|
||||
// If suggestion.nodes is an array of arrays, then there are multiple suggestions being provided
|
||||
// Normalise the shape of the suggestion.nodes to be an array of arrays
|
||||
if (!Array.isArray(suggestion.nodes[0])) {
|
||||
suggestion.nodes = [suggestion.nodes]
|
||||
}
|
||||
suggestion.count = suggestion.nodes.length
|
||||
suggestion.currentIndex = 0
|
||||
suggestion.current = suggestion.nodes[suggestion.currentIndex]
|
||||
refreshSuggestedFlow()
|
||||
} else {
|
||||
redraw()
|
||||
}
|
||||
}
|
||||
|
||||
function refreshSuggestedFlow () {
|
||||
const suggestion = currentSuggestion
|
||||
suggestedNodes = []
|
||||
suggestedLinks = []
|
||||
|
||||
currentSuggestion.current = currentSuggestion.nodes[currentSuggestion.currentIndex]
|
||||
if (suggestion.current.length > 0) {
|
||||
const nodeMap = {}
|
||||
const links = []
|
||||
const positionOffset = { x: 0, y: 0 }
|
||||
@@ -6535,12 +6565,11 @@ RED.view = (function() {
|
||||
targetX += gridOffset.x
|
||||
}
|
||||
|
||||
positionOffset.x = targetX - (suggestion.nodes[0].x || 0)
|
||||
positionOffset.y = targetY - (suggestion.nodes[0].y || 0)
|
||||
positionOffset.x = targetX - (suggestion.current[0].x || 0)
|
||||
positionOffset.y = targetY - (suggestion.current[0].y || 0)
|
||||
}
|
||||
|
||||
|
||||
suggestion.nodes.forEach(nodeConfig => {
|
||||
suggestion.current.forEach(nodeConfig => {
|
||||
if (!nodeConfig.type || nodeConfig.type === 'group' || nodeConfig.type === 'subflow' || nodeConfig.type === 'tab') {
|
||||
// A node type we don't support previewing
|
||||
return
|
||||
@@ -6637,12 +6666,38 @@ RED.view = (function() {
|
||||
})
|
||||
}
|
||||
if (!RED.typeSearch.isVisible()) {
|
||||
// Disable the core keyboard handling so we get priority.
|
||||
// Ideally we'd be able to do this via actions, but we can't currently scope
|
||||
// actions finely enough to only be handled when the suggested flow is active.
|
||||
RED.keyboard.disable()
|
||||
$(window).on('keydown.suggestedFlow', function (evt) {
|
||||
if (evt.keyCode === 9) { // tab
|
||||
$(window).off('keydown.suggestedFlow')
|
||||
RED.keyboard.enable()
|
||||
if (evt.keyCode === 9) { // tab; apply suggestion
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
applySuggestedFlow();
|
||||
} else {
|
||||
} else if (evt.keyCode === 38 && currentSuggestion.count > 1) { // up arrow
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
currentSuggestion.currentIndex--
|
||||
if (currentSuggestion.currentIndex < 0) {
|
||||
currentSuggestion.currentIndex = currentSuggestion.count - 1
|
||||
}
|
||||
refreshSuggestedFlow();
|
||||
} else if (evt.keyCode === 40 && currentSuggestion.count > 1) { // down arrow
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
currentSuggestion.currentIndex++
|
||||
if (currentSuggestion.currentIndex === currentSuggestion.count) {
|
||||
currentSuggestion.currentIndex = 0
|
||||
}
|
||||
refreshSuggestedFlow();
|
||||
} else { // Anything else; clear the suggestion
|
||||
clearSuggestedFlow();
|
||||
RED.view.redraw(true);
|
||||
// manually push the event to the keyboard handler
|
||||
RED.keyboard.handle(evt)
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -6659,20 +6714,27 @@ RED.view = (function() {
|
||||
ghostNode.style('opacity', 1)
|
||||
}
|
||||
}
|
||||
redraw();
|
||||
if (currentSuggestion.count > 1 && suggestedNodes.length > 0) {
|
||||
suggestedNodes[0].status = {
|
||||
text: `${currentSuggestion.currentIndex + 1} / ${currentSuggestion.count}`,
|
||||
}
|
||||
suggestedNodes[0].dirtyStatus = true
|
||||
}
|
||||
redraw()
|
||||
}
|
||||
|
||||
function clearSuggestedFlow () {
|
||||
$(window).off('mousedown.suggestedFlow');
|
||||
$(window).off('keydown.suggestedFlow')
|
||||
RED.keyboard.enable()
|
||||
currentSuggestion = null
|
||||
suggestedNodes = []
|
||||
suggestedLinks = []
|
||||
}
|
||||
|
||||
function applySuggestedFlow () {
|
||||
if (currentSuggestion && currentSuggestion.nodes) {
|
||||
const nodesToImport = currentSuggestion.nodes
|
||||
if (currentSuggestion && currentSuggestion.current) {
|
||||
const nodesToImport = currentSuggestion.current
|
||||
const sourceNode = currentSuggestion.source
|
||||
const sourcePort = currentSuggestion.sourcePort || 0
|
||||
setSuggestedFlow(null)
|
||||
@@ -6681,7 +6743,8 @@ RED.view = (function() {
|
||||
touchImport: true,
|
||||
notify: false,
|
||||
// Ensure the node gets all of its defaults applied
|
||||
applyNodeDefaults: true
|
||||
applyNodeDefaults: true,
|
||||
eventContext: { source: 'suggestion' }
|
||||
})
|
||||
if (sourceNode) {
|
||||
const firstNode = result.nodeMap[nodesToImport[0].id]
|
||||
|
||||
Reference in New Issue
Block a user