mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 11:22:15 +00:00
feat(editor): Add ability to extract sub-workflows to canvas context menu (#15538)
This commit is contained in:
@@ -40,6 +40,7 @@ import {
|
||||
RemoveConnectionCommand,
|
||||
RemoveNodeCommand,
|
||||
RenameNodeCommand,
|
||||
ReplaceNodeParametersCommand,
|
||||
} from '@/models/history';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||
@@ -97,6 +98,7 @@ import type {
|
||||
NodeParameterValueType,
|
||||
Workflow,
|
||||
NodeConnectionType,
|
||||
INodeParameters,
|
||||
} from 'n8n-workflow';
|
||||
import { deepCopy, NodeConnectionTypes, NodeHelpers, TelemetryHelpers } from 'n8n-workflow';
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
@@ -124,6 +126,7 @@ type AddNodesBaseOptions = {
|
||||
trackHistory?: boolean;
|
||||
keepPristine?: boolean;
|
||||
telemetry?: boolean;
|
||||
forcePosition?: boolean;
|
||||
viewport?: ViewportBoundaries;
|
||||
};
|
||||
|
||||
@@ -245,6 +248,42 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
updateNodePosition(node.id, position);
|
||||
}
|
||||
|
||||
function replaceNodeParameters(
|
||||
nodeId: string,
|
||||
currentParameters: INodeParameters,
|
||||
newParameters: INodeParameters,
|
||||
{ trackHistory = false, trackBulk = true } = {},
|
||||
) {
|
||||
const node = workflowsStore.getNodeById(nodeId);
|
||||
if (!node) return;
|
||||
|
||||
if (trackHistory && trackBulk) {
|
||||
historyStore.startRecordingUndo();
|
||||
}
|
||||
workflowsStore.setNodeParameters({
|
||||
name: node.name,
|
||||
value: newParameters,
|
||||
});
|
||||
|
||||
if (trackHistory) {
|
||||
historyStore.pushCommandToUndo(
|
||||
new ReplaceNodeParametersCommand(nodeId, currentParameters, newParameters, Date.now()),
|
||||
);
|
||||
}
|
||||
|
||||
if (trackHistory && trackBulk) {
|
||||
historyStore.stopRecordingUndo();
|
||||
}
|
||||
}
|
||||
|
||||
async function revertReplaceNodeParameters(
|
||||
nodeId: string,
|
||||
currentParameters: INodeParameters,
|
||||
newParameters: INodeParameters,
|
||||
) {
|
||||
replaceNodeParameters(nodeId, newParameters, currentParameters);
|
||||
}
|
||||
|
||||
async function renameNode(
|
||||
currentName: string,
|
||||
newName: string,
|
||||
@@ -419,6 +458,63 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
}
|
||||
}
|
||||
|
||||
function replaceNodeConnections(
|
||||
previousId: string,
|
||||
newId: string,
|
||||
{ trackHistory = false, trackBulk = true, replaceInputs = true, replaceOutputs = true } = {},
|
||||
) {
|
||||
const previousNode = workflowsStore.getNodeById(previousId);
|
||||
const newNode = workflowsStore.getNodeById(newId);
|
||||
|
||||
if (!previousNode || !newNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wf = workflowsStore.getCurrentWorkflow();
|
||||
|
||||
const inputNodeNames = replaceInputs ? wf.getParentNodes(previousNode.name, 'main', 1) : [];
|
||||
const outputNodeNames = replaceOutputs ? wf.getChildNodes(previousNode.name, 'main', 1) : [];
|
||||
const connectionPairs = [
|
||||
...wf.getConnectionsBetweenNodes(inputNodeNames, [previousNode.name]),
|
||||
...wf.getConnectionsBetweenNodes([previousNode.name], outputNodeNames),
|
||||
];
|
||||
|
||||
if (trackHistory && trackBulk) {
|
||||
historyStore.startRecordingUndo();
|
||||
}
|
||||
for (const pair of connectionPairs) {
|
||||
const sourceNode = workflowsStore.getNodeByName(pair[0].node);
|
||||
const targetNode = workflowsStore.getNodeByName(pair[1].node);
|
||||
if (!sourceNode || !targetNode) continue;
|
||||
const oldCanvasConnection = mapLegacyConnectionToCanvasConnection(
|
||||
sourceNode,
|
||||
targetNode,
|
||||
pair,
|
||||
);
|
||||
deleteConnection(oldCanvasConnection, { trackHistory, trackBulk: false });
|
||||
|
||||
const newCanvasConnection = mapLegacyConnectionToCanvasConnection(
|
||||
sourceNode.name === previousNode.name ? newNode : sourceNode,
|
||||
targetNode.name === previousNode.name ? newNode : targetNode,
|
||||
[
|
||||
{
|
||||
...pair[0],
|
||||
node: pair[0].node === previousNode.name ? newNode.name : pair[0].node,
|
||||
},
|
||||
{
|
||||
...pair[1],
|
||||
node: pair[1].node === previousNode.name ? newNode.name : pair[1].node,
|
||||
},
|
||||
],
|
||||
);
|
||||
createConnection(newCanvasConnection, { trackHistory });
|
||||
}
|
||||
|
||||
if (trackHistory && trackBulk) {
|
||||
historyStore.stopRecordingUndo();
|
||||
}
|
||||
}
|
||||
|
||||
function setNodeActive(id: string) {
|
||||
const node = workflowsStore.getNodeById(id);
|
||||
if (!node) {
|
||||
@@ -710,7 +806,6 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
const lastInteractedWithNodeId = lastInteractedWithNode.id;
|
||||
const lastInteractedWithNodeConnection = uiStore.lastInteractedWithNodeConnection;
|
||||
const lastInteractedWithNodeHandle = uiStore.lastInteractedWithNodeHandle;
|
||||
|
||||
// If we have a specific endpoint to connect to
|
||||
if (lastInteractedWithNodeHandle) {
|
||||
const { type: connectionType, mode } = parseCanvasConnectionHandleString(
|
||||
@@ -813,15 +908,18 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
function resolveNodeData(
|
||||
node: AddNodeDataWithTypeVersion,
|
||||
nodeTypeDescription: INodeTypeDescription,
|
||||
options: { viewport?: ViewportBoundaries } = {},
|
||||
options: { viewport?: ViewportBoundaries; forcePosition?: boolean } = {},
|
||||
) {
|
||||
const id = node.id ?? nodeHelpers.assignNodeId(node as INodeUi);
|
||||
const name = node.name ?? (nodeTypeDescription.defaults.name as string);
|
||||
const type = nodeTypeDescription.name;
|
||||
const typeVersion = node.typeVersion;
|
||||
const position = resolveNodePosition(node as INodeUi, nodeTypeDescription, {
|
||||
viewport: options.viewport,
|
||||
});
|
||||
const position =
|
||||
options.forcePosition && node.position
|
||||
? node.position
|
||||
: resolveNodePosition(node as INodeUi, nodeTypeDescription, {
|
||||
viewport: options.viewport,
|
||||
});
|
||||
const disabled = node.disabled ?? false;
|
||||
const parameters = node.parameters ?? {};
|
||||
|
||||
@@ -2110,6 +2208,8 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
setNodeParameters,
|
||||
renameNode,
|
||||
revertRenameNode,
|
||||
replaceNodeParameters,
|
||||
revertReplaceNodeParameters,
|
||||
deleteNode,
|
||||
deleteNodes,
|
||||
copyNodes,
|
||||
@@ -2136,6 +2236,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
openExecution,
|
||||
startChat,
|
||||
importTemplate,
|
||||
replaceNodeConnections,
|
||||
tryToOpenSubworkflowInNewTab,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user