mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
feat(editor): Add ability to add a node between two nodes in new canvas (no-changelog) (#10006)
This commit is contained in:
parent
d651be4e01
commit
1aae65dfdc
|
@ -24,6 +24,7 @@ const emit = defineEmits<{
|
|||
'create:connection': [connection: Connection];
|
||||
'create:connection:end': [connection: Connection];
|
||||
'create:connection:cancelled': [handle: ConnectStartEvent];
|
||||
'click:connection:add': [connection: Connection];
|
||||
'click:pane': [position: XYPosition];
|
||||
}>();
|
||||
|
||||
|
@ -123,6 +124,10 @@ function onDeleteConnection(connection: Connection) {
|
|||
emit('delete:connection', connection);
|
||||
}
|
||||
|
||||
function onClickConnectionAdd(connection: Connection) {
|
||||
emit('click:connection:add', connection);
|
||||
}
|
||||
|
||||
function onRunNode(id: string) {
|
||||
emit('run:node', id);
|
||||
}
|
||||
|
@ -190,6 +195,7 @@ function onClickPane(event: MouseEvent) {
|
|||
<Edge
|
||||
v-bind="canvasEdgeProps"
|
||||
:hovered="hoveredEdges[canvasEdgeProps.id]"
|
||||
@add="onClickConnectionAdd"
|
||||
@delete="onDeleteConnection"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -5,8 +5,11 @@ import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@vue-flow/core';
|
|||
import CanvasEdgeToolbar from './CanvasEdgeToolbar.vue';
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import type { CanvasConnectionData } from '@/types';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import { isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||
|
||||
const emit = defineEmits<{
|
||||
add: [connection: Connection];
|
||||
delete: [connection: Connection];
|
||||
}>();
|
||||
|
||||
|
@ -18,6 +21,12 @@ const props = defineProps<CanvasEdgeProps>();
|
|||
|
||||
const $style = useCssModule();
|
||||
|
||||
const connectionType = computed(() =>
|
||||
isValidNodeConnectionType(props.data.source.type)
|
||||
? props.data.source.type
|
||||
: NodeConnectionType.Main,
|
||||
);
|
||||
|
||||
const isFocused = computed(() => props.selected || props.hovered);
|
||||
|
||||
const status = computed(() => props.data.status);
|
||||
|
@ -86,6 +95,10 @@ const connection = computed<Connection>(() => ({
|
|||
targetHandle: props.targetHandleId,
|
||||
}));
|
||||
|
||||
function onAdd() {
|
||||
emit('add', connection.value);
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
emit('delete', connection.value);
|
||||
}
|
||||
|
@ -105,7 +118,13 @@ function onDelete() {
|
|||
:label-show-bg="false"
|
||||
/>
|
||||
<EdgeLabelRenderer>
|
||||
<CanvasEdgeToolbar :class="edgeToolbarClasses" :style="edgeToolbarStyle" @delete="onDelete" />
|
||||
<CanvasEdgeToolbar
|
||||
:type="connectionType"
|
||||
:class="edgeToolbarClasses"
|
||||
:style="edgeToolbarStyle"
|
||||
@add="onAdd"
|
||||
@delete="onDelete"
|
||||
/>
|
||||
</EdgeLabelRenderer>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
const emit = defineEmits<{
|
||||
add: [];
|
||||
delete: [];
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
type: NodeConnectionType;
|
||||
}>();
|
||||
|
||||
const $style = useCssModule();
|
||||
|
||||
const i18n = useI18n();
|
||||
|
@ -14,6 +20,12 @@ const classes = computed(() => ({
|
|||
[$style.canvasEdgeToolbar]: true,
|
||||
}));
|
||||
|
||||
const isAddButtonVisible = computed(() => props.type === NodeConnectionType.Main);
|
||||
|
||||
function onAdd() {
|
||||
emit('add');
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
emit('delete');
|
||||
}
|
||||
|
@ -21,6 +33,15 @@ function onDelete() {
|
|||
|
||||
<template>
|
||||
<div :class="classes" data-test-id="canvas-edge-toolbar">
|
||||
<N8nIconButton
|
||||
v-if="isAddButtonVisible"
|
||||
data-test-id="add-connection-button"
|
||||
type="tertiary"
|
||||
size="small"
|
||||
icon="plus"
|
||||
:title="i18n.baseText('node.add')"
|
||||
@click="onAdd"
|
||||
/>
|
||||
<N8nIconButton
|
||||
data-test-id="delete-connection-button"
|
||||
type="tertiary"
|
||||
|
@ -37,6 +58,7 @@ function onDelete() {
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2xs);
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -15,6 +15,11 @@ import {
|
|||
import { MANUAL_TRIGGER_NODE_TYPE, SET_NODE_TYPE } from '@/constants';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import {
|
||||
createCanvasConnectionHandleString,
|
||||
createCanvasConnectionId,
|
||||
} from '@/utils/canvasUtilsV2';
|
||||
import { CanvasConnectionMode } from '@/types';
|
||||
|
||||
beforeEach(() => {
|
||||
const pinia = createPinia();
|
||||
|
@ -243,6 +248,25 @@ describe('useCanvasMapping', () => {
|
|||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||
});
|
||||
|
||||
const source = manualTriggerNode.id;
|
||||
const sourceHandle = createCanvasConnectionHandleString({
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
mode: CanvasConnectionMode.Output,
|
||||
});
|
||||
const target = setNode.id;
|
||||
const targetHandle = createCanvasConnectionHandleString({
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
mode: CanvasConnectionMode.Input,
|
||||
});
|
||||
const connectionId = createCanvasConnectionId({
|
||||
source,
|
||||
sourceHandle,
|
||||
target,
|
||||
targetHandle,
|
||||
});
|
||||
|
||||
expect(mappedConnections.value).toEqual([
|
||||
{
|
||||
data: {
|
||||
|
@ -257,12 +281,12 @@ describe('useCanvasMapping', () => {
|
|||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
id: `[${manualTriggerNode.id}/${NodeConnectionType.Main}/0][${setNode.id}/${NodeConnectionType.Main}/0]`,
|
||||
id: connectionId,
|
||||
label: '',
|
||||
source: manualTriggerNode.id,
|
||||
sourceHandle: `outputs/${NodeConnectionType.Main}/0`,
|
||||
target: setNode.id,
|
||||
targetHandle: `inputs/${NodeConnectionType.Main}/0`,
|
||||
source,
|
||||
sourceHandle,
|
||||
target,
|
||||
targetHandle,
|
||||
type: 'canvas-edge',
|
||||
animated: false,
|
||||
},
|
||||
|
@ -293,6 +317,44 @@ describe('useCanvasMapping', () => {
|
|||
workflowObject: ref(workflowObject) as Ref<Workflow>,
|
||||
});
|
||||
|
||||
const sourceA = manualTriggerNode.id;
|
||||
const sourceHandleA = createCanvasConnectionHandleString({
|
||||
type: NodeConnectionType.AiTool,
|
||||
index: 0,
|
||||
mode: CanvasConnectionMode.Output,
|
||||
});
|
||||
const targetA = setNode.id;
|
||||
const targetHandleA = createCanvasConnectionHandleString({
|
||||
type: NodeConnectionType.AiTool,
|
||||
index: 0,
|
||||
mode: CanvasConnectionMode.Input,
|
||||
});
|
||||
const connectionIdA = createCanvasConnectionId({
|
||||
source: sourceA,
|
||||
sourceHandle: sourceHandleA,
|
||||
target: targetA,
|
||||
targetHandle: targetHandleA,
|
||||
});
|
||||
|
||||
const sourceB = manualTriggerNode.id;
|
||||
const sourceHandleB = createCanvasConnectionHandleString({
|
||||
type: NodeConnectionType.AiDocument,
|
||||
index: 0,
|
||||
mode: CanvasConnectionMode.Output,
|
||||
});
|
||||
const targetB = setNode.id;
|
||||
const targetHandleB = createCanvasConnectionHandleString({
|
||||
type: NodeConnectionType.AiDocument,
|
||||
index: 1,
|
||||
mode: CanvasConnectionMode.Input,
|
||||
});
|
||||
const connectionIdB = createCanvasConnectionId({
|
||||
source: sourceB,
|
||||
sourceHandle: sourceHandleB,
|
||||
target: targetB,
|
||||
targetHandle: targetHandleB,
|
||||
});
|
||||
|
||||
expect(mappedConnections.value).toEqual([
|
||||
{
|
||||
data: {
|
||||
|
@ -307,12 +369,12 @@ describe('useCanvasMapping', () => {
|
|||
type: NodeConnectionType.AiTool,
|
||||
},
|
||||
},
|
||||
id: `[${manualTriggerNode.id}/${NodeConnectionType.AiTool}/0][${setNode.id}/${NodeConnectionType.AiTool}/0]`,
|
||||
id: connectionIdA,
|
||||
label: '',
|
||||
source: manualTriggerNode.id,
|
||||
sourceHandle: `outputs/${NodeConnectionType.AiTool}/0`,
|
||||
target: setNode.id,
|
||||
targetHandle: `inputs/${NodeConnectionType.AiTool}/0`,
|
||||
source: sourceA,
|
||||
sourceHandle: sourceHandleA,
|
||||
target: targetA,
|
||||
targetHandle: targetHandleA,
|
||||
type: 'canvas-edge',
|
||||
animated: false,
|
||||
},
|
||||
|
@ -329,12 +391,12 @@ describe('useCanvasMapping', () => {
|
|||
type: NodeConnectionType.AiDocument,
|
||||
},
|
||||
},
|
||||
id: `[${manualTriggerNode.id}/${NodeConnectionType.AiDocument}/0][${setNode.id}/${NodeConnectionType.AiDocument}/1]`,
|
||||
id: connectionIdB,
|
||||
label: '',
|
||||
source: manualTriggerNode.id,
|
||||
sourceHandle: `outputs/${NodeConnectionType.AiDocument}/0`,
|
||||
target: setNode.id,
|
||||
targetHandle: `inputs/${NodeConnectionType.AiDocument}/1`,
|
||||
source: sourceB,
|
||||
sourceHandle: sourceHandleB,
|
||||
target: targetB,
|
||||
targetHandle: targetHandleB,
|
||||
type: 'canvas-edge',
|
||||
animated: false,
|
||||
},
|
||||
|
|
|
@ -5,12 +5,7 @@
|
|||
|
||||
import type { CanvasNode } from '@/types';
|
||||
import { CanvasConnectionMode } from '@/types';
|
||||
import type {
|
||||
AddedNodesAndConnections,
|
||||
INodeUi,
|
||||
INodeUpdatePropertiesInformation,
|
||||
XYPosition,
|
||||
} from '@/Interface';
|
||||
import type { AddedNodesAndConnections, INodeUi, XYPosition } from '@/Interface';
|
||||
import {
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
QUICKSTART_NOTE_NAME,
|
||||
|
@ -31,7 +26,9 @@ import {
|
|||
} from '@/models/history';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import {
|
||||
createCanvasConnectionHandleString,
|
||||
getUniqueNodeName,
|
||||
getVueFlowConnectorLengths,
|
||||
mapCanvasConnectionToLegacyConnection,
|
||||
parseCanvasConnectionHandleString,
|
||||
} from '@/utils/canvasUtilsV2';
|
||||
|
@ -334,6 +331,7 @@ export function useCanvasOperations({
|
|||
nodeHelpers.matchCredentials(newNodeData);
|
||||
|
||||
const lastSelectedNode = uiStore.getLastSelectedNode;
|
||||
const lastSelectedNodeConnection = uiStore.lastSelectedNodeConnection;
|
||||
const lastSelectedNodeOutputIndex = uiStore.lastSelectedNodeOutputIndex;
|
||||
const lastSelectedNodeEndpointUuid = uiStore.lastSelectedNodeEndpointUuid;
|
||||
|
||||
|
@ -378,11 +376,37 @@ export function useCanvasOperations({
|
|||
// Connect active node to the newly created one
|
||||
createConnection({
|
||||
source: lastSelectedNode.id,
|
||||
sourceHandle: `outputs/${NodeConnectionType.Main}/${outputIndex}`,
|
||||
sourceHandle: createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: outputIndex,
|
||||
}),
|
||||
target: newNodeData.id,
|
||||
targetHandle: `inputs/${NodeConnectionType.Main}/0`,
|
||||
targetHandle: createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (lastSelectedNodeConnection) {
|
||||
deleteConnection(lastSelectedNodeConnection, { trackHistory: options.trackHistory });
|
||||
|
||||
const targetNode = workflowsStore.getNodeById(lastSelectedNodeConnection.target);
|
||||
if (targetNode) {
|
||||
createConnection({
|
||||
source: newNodeData.id,
|
||||
sourceHandle: createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
}),
|
||||
target: lastSelectedNodeConnection.target,
|
||||
targetHandle: lastSelectedNodeConnection.targetHandle,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
historyStore.stopRecordingUndo();
|
||||
|
@ -516,11 +540,10 @@ export function useCanvasOperations({
|
|||
node.position,
|
||||
);
|
||||
} else if (lastSelectedNode) {
|
||||
// @TODO Implement settings lastSelectedConnection for new canvas
|
||||
const lastSelectedConnection = canvasStore.lastSelectedConnection;
|
||||
if (lastSelectedConnection) {
|
||||
if (uiStore.lastSelectedNodeConnection) {
|
||||
// set when injecting into a connection
|
||||
const [diffX] = NodeViewUtils.getConnectorLengths(lastSelectedConnection);
|
||||
const [diffX] = getVueFlowConnectorLengths(uiStore.lastSelectedNodeConnection);
|
||||
|
||||
if (diffX <= NodeViewUtils.MAX_X_TO_PUSH_DOWNSTREAM_NODES) {
|
||||
pushDownstreamNodes(lastSelectedNode.name, NodeViewUtils.PUSH_NODES_OFFSET, {
|
||||
trackHistory: options.trackHistory,
|
||||
|
@ -537,7 +560,7 @@ export function useCanvasOperations({
|
|||
canvasStore.newNodeInsertPosition = null;
|
||||
} else {
|
||||
let yOffset = 0;
|
||||
if (lastSelectedConnection) {
|
||||
if (uiStore.lastSelectedNodeConnection) {
|
||||
const sourceNodeType = nodeTypesStore.getNodeType(
|
||||
lastSelectedNode.type,
|
||||
lastSelectedNode.typeVersion,
|
||||
|
@ -556,16 +579,15 @@ export function useCanvasOperations({
|
|||
sourceNodeType,
|
||||
);
|
||||
const sourceNodeOutputTypes = NodeHelpers.getConnectionTypes(sourceNodeOutputs);
|
||||
|
||||
const sourceNodeOutputMainOutputs = sourceNodeOutputTypes.filter(
|
||||
(output) => output === NodeConnectionType.Main,
|
||||
);
|
||||
|
||||
if (sourceNodeOutputMainOutputs.length > 1) {
|
||||
const { index: sourceOutputIndex } = parseCanvasConnectionHandleString(
|
||||
uiStore.lastSelectedNodeConnection.sourceHandle,
|
||||
);
|
||||
const offset = offsets[sourceNodeOutputMainOutputs.length - 2];
|
||||
const sourceOutputIndex = lastSelectedConnection.__meta
|
||||
? lastSelectedConnection.__meta.sourceOutputIndex
|
||||
: 0;
|
||||
yOffset = offset[sourceOutputIndex];
|
||||
}
|
||||
}
|
||||
|
@ -729,33 +751,20 @@ export function useCanvasOperations({
|
|||
);
|
||||
for (const nodeName of checkNodes) {
|
||||
const node = workflowsStore.nodesByName[nodeName];
|
||||
const oldPosition = node.position;
|
||||
|
||||
if (node.position[0] < sourceNode.position[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const updateInformation: INodeUpdatePropertiesInformation = {
|
||||
name: nodeName,
|
||||
properties: {
|
||||
position: [node.position[0] + margin, node.position[1]],
|
||||
updateNodePosition(
|
||||
node.id,
|
||||
{
|
||||
x: node.position[0] + margin,
|
||||
y: node.position[1],
|
||||
},
|
||||
};
|
||||
|
||||
workflowsStore.updateNodeProperties(updateInformation);
|
||||
updateNodePosition(node.id, { x: node.position[0], y: node.position[1] });
|
||||
|
||||
if (
|
||||
(trackHistory && oldPosition[0] !== updateInformation.properties.position[0]) ||
|
||||
oldPosition[1] !== updateInformation.properties.position[1]
|
||||
) {
|
||||
historyStore.pushCommandToUndo(
|
||||
new MoveNodeCommand(nodeName, oldPosition, updateInformation.properties.position),
|
||||
trackHistory,
|
||||
{ trackHistory },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadNodeTypesProperties(nodeInfos: INodeTypeNameVersion[]): Promise<void> {
|
||||
const allNodeTypeDescriptions: INodeTypeDescription[] = nodeTypesStore.allNodeTypes;
|
||||
|
|
|
@ -960,6 +960,7 @@
|
|||
"node.disable": "Deactivate",
|
||||
"node.enable": "Activate",
|
||||
"node.delete": "Delete",
|
||||
"node.add": "Add",
|
||||
"node.issues": "Issues",
|
||||
"node.nodeIsExecuting": "Node is executing",
|
||||
"node.nodeIsWaitingTill": "Node is waiting until {date} {time}",
|
||||
|
|
|
@ -12,7 +12,6 @@ import type {
|
|||
SimplifiedNodeType,
|
||||
ActionsRecord,
|
||||
ToggleNodeCreatorOptions,
|
||||
NewConnectionInfo,
|
||||
} from '@/Interface';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
@ -26,7 +25,14 @@ import { useExternalHooks } from '@/composables/useExternalHooks';
|
|||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useViewStacks } from '@/components/Node/NodeCreator/composables/useViewStacks';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { createCanvasConnectionHandleString } from '@/utils/canvasUtilsV2';
|
||||
import {
|
||||
createCanvasConnectionHandleString,
|
||||
parseCanvasConnectionHandleString,
|
||||
} from '@/utils/canvasUtilsV2';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import { CanvasConnectionMode } from '@/types';
|
||||
import { isVueFlowConnection } from '@/utils/typeGuards';
|
||||
import type { PartialBy } from '@/utils/typeHelpers';
|
||||
|
||||
export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
@ -90,15 +96,15 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
|
|||
});
|
||||
} else if (connectionType && nodeData) {
|
||||
openNodeCreatorForConnectingNode({
|
||||
index: 0,
|
||||
endpointUuid: createCanvasConnectionHandleString({
|
||||
connection: {
|
||||
source: nodeData.id,
|
||||
sourceHandle: createCanvasConnectionHandleString({
|
||||
mode: 'inputs',
|
||||
type: connectionType,
|
||||
index: 0,
|
||||
}),
|
||||
},
|
||||
eventSource: NODE_CREATOR_OPEN_SOURCES.NOTICE_ERROR_MESSAGE,
|
||||
outputType: connectionType,
|
||||
sourceId: nodeData.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -172,35 +178,43 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
|
|||
});
|
||||
}
|
||||
|
||||
function openNodeCreatorForConnectingNode(info: NewConnectionInfo) {
|
||||
const type = info.outputType ?? NodeConnectionType.Main;
|
||||
function openNodeCreatorForConnectingNode({
|
||||
connection,
|
||||
eventSource,
|
||||
nodeCreatorView,
|
||||
}: {
|
||||
connection: PartialBy<Connection, 'target' | 'targetHandle'>;
|
||||
eventSource?: NodeCreatorOpenSource;
|
||||
nodeCreatorView?: NodeFilterType;
|
||||
}) {
|
||||
// Get the node and set it as active that new nodes
|
||||
// which get created get automatically connected
|
||||
// to it.
|
||||
const sourceNode = workflowsStore.getNodeById(info.sourceId);
|
||||
const sourceNode = workflowsStore.getNodeById(connection.source);
|
||||
if (!sourceNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, index, mode } = parseCanvasConnectionHandleString(connection.sourceHandle);
|
||||
|
||||
uiStore.lastSelectedNode = sourceNode.name;
|
||||
uiStore.lastSelectedNodeEndpointUuid = info.endpointUuid ?? null;
|
||||
uiStore.lastSelectedNodeOutputIndex = info.index;
|
||||
uiStore.lastSelectedNodeEndpointUuid = connection.sourceHandle ?? null;
|
||||
uiStore.lastSelectedNodeOutputIndex = index;
|
||||
// canvasStore.newNodeInsertPosition = null;
|
||||
|
||||
// @TODO Add connection to store
|
||||
// if (info.connection) {
|
||||
// canvasStore.setLastSelectedConnection(info.connection);
|
||||
// }
|
||||
if (isVueFlowConnection(connection)) {
|
||||
uiStore.lastSelectedNodeConnection = connection;
|
||||
}
|
||||
|
||||
openNodeCreator({
|
||||
source: info.eventSource,
|
||||
source: eventSource,
|
||||
createNodeActive: true,
|
||||
nodeCreatorView: info.nodeCreatorView,
|
||||
nodeCreatorView,
|
||||
});
|
||||
|
||||
// TODO: The animation is a bit glitchy because we're updating view stack immediately
|
||||
// after the node creator is opened
|
||||
const isOutput = info.connection?.endpoints[0].parameters.connection === 'source';
|
||||
const isOutput = mode === CanvasConnectionMode.Output;
|
||||
const isScopedConnection =
|
||||
type !== NodeConnectionType.Main && nodeConnectionTypes.includes(type);
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ import {
|
|||
updateTheme,
|
||||
} from './ui.utils';
|
||||
import { computed, ref } from 'vue';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
|
||||
let savedTheme: ThemeOption = 'system';
|
||||
try {
|
||||
|
@ -177,6 +178,7 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||
const lastSelectedNode = ref<string | null>(null);
|
||||
const lastSelectedNodeOutputIndex = ref<number | null>(null);
|
||||
const lastSelectedNodeEndpointUuid = ref<string | null>(null);
|
||||
const lastSelectedNodeConnection = ref<Connection | null>(null);
|
||||
const nodeViewOffsetPosition = ref<[number, number]>([0, 0]);
|
||||
const nodeViewMoveInProgress = ref<boolean>(false);
|
||||
const selectedNodes = ref<INodeUi[]>([]);
|
||||
|
@ -619,6 +621,7 @@ export const useUIStore = defineStore(STORES.UI, () => {
|
|||
selectedNodes,
|
||||
bannersHeight,
|
||||
lastSelectedNodeEndpointUuid,
|
||||
lastSelectedNodeConnection,
|
||||
nodeViewOffsetPosition,
|
||||
nodeViewMoveInProgress,
|
||||
nodeViewInitialized,
|
||||
|
|
|
@ -5,12 +5,14 @@ import {
|
|||
mapCanvasConnectionToLegacyConnection,
|
||||
parseCanvasConnectionHandleString,
|
||||
createCanvasConnectionHandleString,
|
||||
createCanvasConnectionId,
|
||||
} from '@/utils/canvasUtilsV2';
|
||||
import { NodeConnectionType, type IConnections, type INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { CanvasConnection } from '@/types';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import { createTestNode } from '@/__tests__/mocks';
|
||||
import { CanvasConnectionMode } from '@/types';
|
||||
|
||||
vi.mock('uuid', () => ({
|
||||
v4: vi.fn(() => 'mock-uuid'),
|
||||
|
@ -47,15 +49,34 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
nodes,
|
||||
);
|
||||
|
||||
const source = nodes[0].id;
|
||||
const sourceHandle = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const target = nodes[1].id;
|
||||
const targetHandle = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const id = createCanvasConnectionId({
|
||||
source,
|
||||
target,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: '[1/main/0][2/main/0]',
|
||||
source: '1',
|
||||
target: '2',
|
||||
sourceHandle: 'outputs/main/0',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id,
|
||||
source,
|
||||
target,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 0,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -147,15 +168,53 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
nodes,
|
||||
);
|
||||
|
||||
const sourceA = nodes[0].id;
|
||||
const sourceHandleA = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const targetA = nodes[1].id;
|
||||
const targetHandleA = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const connectionIdA = createCanvasConnectionId({
|
||||
source: sourceA,
|
||||
target: targetA,
|
||||
sourceHandle: sourceHandleA,
|
||||
targetHandle: targetHandleA,
|
||||
});
|
||||
|
||||
const sourceB = nodes[0].id;
|
||||
const sourceHandleB = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 1,
|
||||
});
|
||||
const targetB = nodes[1].id;
|
||||
const targetHandleB = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 1,
|
||||
});
|
||||
const connectionIdB = createCanvasConnectionId({
|
||||
source: sourceB,
|
||||
target: targetB,
|
||||
sourceHandle: sourceHandleB,
|
||||
targetHandle: targetHandleB,
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: '[1/main/0][2/main/0]',
|
||||
source: '1',
|
||||
target: '2',
|
||||
sourceHandle: 'outputs/main/0',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id: connectionIdA,
|
||||
source: sourceA,
|
||||
target: targetA,
|
||||
sourceHandle: sourceHandleA,
|
||||
targetHandle: targetHandleA,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 0,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -167,13 +226,13 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
id: '[1/main/1][2/main/1]',
|
||||
source: '1',
|
||||
target: '2',
|
||||
sourceHandle: 'outputs/main/1',
|
||||
targetHandle: 'inputs/main/1',
|
||||
id: connectionIdB,
|
||||
source: sourceA,
|
||||
target: targetB,
|
||||
sourceHandle: sourceHandleB,
|
||||
targetHandle: targetHandleB,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 1,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -228,15 +287,53 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
nodes,
|
||||
);
|
||||
|
||||
const sourceA = nodes[0].id;
|
||||
const sourceHandleA = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const targetA = nodes[1].id;
|
||||
const targetHandleA = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const connectionIdA = createCanvasConnectionId({
|
||||
source: sourceA,
|
||||
target: targetA,
|
||||
sourceHandle: sourceHandleA,
|
||||
targetHandle: targetHandleA,
|
||||
});
|
||||
|
||||
const sourceB = nodes[0].id;
|
||||
const sourceHandleB = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 1,
|
||||
});
|
||||
const targetB = nodes[2].id;
|
||||
const targetHandleB = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const connectionIdB = createCanvasConnectionId({
|
||||
source: sourceB,
|
||||
target: targetB,
|
||||
sourceHandle: sourceHandleB,
|
||||
targetHandle: targetHandleB,
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: '[1/main/0][2/main/0]',
|
||||
source: '1',
|
||||
target: '2',
|
||||
sourceHandle: 'outputs/main/0',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id: connectionIdA,
|
||||
source: sourceA,
|
||||
target: targetA,
|
||||
sourceHandle: sourceHandleA,
|
||||
targetHandle: targetHandleA,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 0,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -248,13 +345,13 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
id: '[1/main/1][3/main/0]',
|
||||
source: '1',
|
||||
target: '3',
|
||||
sourceHandle: 'outputs/main/1',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id: connectionIdB,
|
||||
source: sourceB,
|
||||
target: targetB,
|
||||
sourceHandle: sourceHandleB,
|
||||
targetHandle: targetHandleB,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 1,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -312,15 +409,72 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
nodes,
|
||||
);
|
||||
|
||||
const sourceA = nodes[0].id;
|
||||
const sourceHandleA = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const targetA = nodes[1].id;
|
||||
const targetHandleA = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const connectionIdA = createCanvasConnectionId({
|
||||
source: sourceA,
|
||||
target: targetA,
|
||||
sourceHandle: sourceHandleA,
|
||||
targetHandle: targetHandleA,
|
||||
});
|
||||
|
||||
const sourceB = nodes[0].id;
|
||||
const sourceHandleB = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.AiMemory,
|
||||
index: 0,
|
||||
});
|
||||
const targetB = nodes[2].id;
|
||||
const targetHandleB = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.AiMemory,
|
||||
index: 1,
|
||||
});
|
||||
const connectionIdB = createCanvasConnectionId({
|
||||
source: sourceB,
|
||||
target: targetB,
|
||||
sourceHandle: sourceHandleB,
|
||||
targetHandle: targetHandleB,
|
||||
});
|
||||
|
||||
const sourceC = nodes[1].id;
|
||||
const sourceHandleC = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const targetC = nodes[2].id;
|
||||
const targetHandleC = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
const connectionIdC = createCanvasConnectionId({
|
||||
source: sourceC,
|
||||
target: targetC,
|
||||
sourceHandle: sourceHandleC,
|
||||
targetHandle: targetHandleC,
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: '[1/main/0][2/main/0]',
|
||||
source: '1',
|
||||
target: '2',
|
||||
sourceHandle: 'outputs/main/0',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id: connectionIdA,
|
||||
source: sourceA,
|
||||
target: targetA,
|
||||
sourceHandle: sourceHandleA,
|
||||
targetHandle: targetHandleA,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 0,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -332,13 +486,13 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
id: `[1/${NodeConnectionType.AiMemory}/0][3/${NodeConnectionType.AiMemory}/1]`,
|
||||
source: '1',
|
||||
target: '3',
|
||||
sourceHandle: `outputs/${NodeConnectionType.AiMemory}/0`,
|
||||
targetHandle: `inputs/${NodeConnectionType.AiMemory}/1`,
|
||||
id: connectionIdB,
|
||||
source: sourceB,
|
||||
target: targetB,
|
||||
sourceHandle: sourceHandleB,
|
||||
targetHandle: targetHandleB,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 0,
|
||||
type: NodeConnectionType.AiMemory,
|
||||
|
@ -350,13 +504,13 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
id: '[2/main/0][3/main/0]',
|
||||
source: '2',
|
||||
target: '3',
|
||||
sourceHandle: 'outputs/main/0',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id: connectionIdC,
|
||||
source: sourceC,
|
||||
target: targetC,
|
||||
sourceHandle: sourceHandleC,
|
||||
targetHandle: targetHandleC,
|
||||
data: {
|
||||
fromNodeName: 'Node B',
|
||||
fromNodeName: nodes[1].name,
|
||||
source: {
|
||||
index: 0,
|
||||
type: NodeConnectionType.Main,
|
||||
|
@ -403,15 +557,36 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
|||
nodes,
|
||||
);
|
||||
|
||||
const source = nodes[0].id;
|
||||
const sourceHandle = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 1,
|
||||
});
|
||||
|
||||
const target = nodes[1].id;
|
||||
const targetHandle = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
});
|
||||
|
||||
const id = createCanvasConnectionId({
|
||||
source,
|
||||
target,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: '[1/main/1][2/main/0]',
|
||||
source: '1',
|
||||
target: '2',
|
||||
sourceHandle: 'outputs/main/1',
|
||||
targetHandle: 'inputs/main/0',
|
||||
id,
|
||||
source,
|
||||
target,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
fromNodeName: nodes[0].name,
|
||||
source: {
|
||||
index: 1,
|
||||
type: NodeConnectionType.Main,
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import type { IConnection, IConnections, INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import type { CanvasConnection, CanvasConnectionPortType, CanvasConnectionPort } from '@/types';
|
||||
import type { CanvasConnection, CanvasConnectionPort } from '@/types';
|
||||
import { CanvasConnectionMode } from '@/types';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { isValidCanvasConnectionMode, isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { Connection as VueFlowConnection } from '@vue-flow/core/dist/types/connection';
|
||||
import { PUSH_NODES_OFFSET } from '@/utils/nodeViewUtils';
|
||||
|
||||
export function mapLegacyConnectionsToCanvasConnections(
|
||||
legacyConnections: IConnections,
|
||||
|
@ -14,33 +16,54 @@ export function mapLegacyConnectionsToCanvasConnections(
|
|||
const mappedConnections: CanvasConnection[] = [];
|
||||
|
||||
Object.keys(legacyConnections).forEach((fromNodeName) => {
|
||||
const fromId = nodes.find((node) => node.name === fromNodeName)?.id;
|
||||
const fromConnectionTypes = Object.keys(legacyConnections[fromNodeName]);
|
||||
const fromId = nodes.find((node) => node.name === fromNodeName)?.id ?? '';
|
||||
const fromConnectionTypes = Object.keys(
|
||||
legacyConnections[fromNodeName],
|
||||
) as NodeConnectionType[];
|
||||
|
||||
fromConnectionTypes.forEach((fromConnectionType) => {
|
||||
const fromPorts = legacyConnections[fromNodeName][fromConnectionType];
|
||||
fromPorts.forEach((toPorts, fromIndex) => {
|
||||
toPorts.forEach((toPort) => {
|
||||
const toId = nodes.find((node) => node.name === toPort.node)?.id;
|
||||
const toConnectionType = toPort.type;
|
||||
const toId = nodes.find((node) => node.name === toPort.node)?.id ?? '';
|
||||
const toConnectionType = toPort.type as NodeConnectionType;
|
||||
const toIndex = toPort.index;
|
||||
|
||||
const sourceHandle = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Output,
|
||||
type: fromConnectionType,
|
||||
index: fromIndex,
|
||||
});
|
||||
|
||||
const targetHandle = createCanvasConnectionHandleString({
|
||||
mode: CanvasConnectionMode.Input,
|
||||
type: toConnectionType,
|
||||
index: toIndex,
|
||||
});
|
||||
|
||||
const connectionId = createCanvasConnectionId({
|
||||
source: fromId,
|
||||
sourceHandle,
|
||||
target: toId,
|
||||
targetHandle,
|
||||
});
|
||||
|
||||
if (fromId && toId) {
|
||||
mappedConnections.push({
|
||||
id: `[${fromId}/${fromConnectionType}/${fromIndex}][${toId}/${toConnectionType}/${toIndex}]`,
|
||||
id: connectionId,
|
||||
source: fromId,
|
||||
target: toId,
|
||||
sourceHandle: `${CanvasConnectionMode.Output}/${fromConnectionType}/${fromIndex}`,
|
||||
targetHandle: `${CanvasConnectionMode.Input}/${toConnectionType}/${toIndex}`,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
data: {
|
||||
fromNodeName,
|
||||
source: {
|
||||
index: fromIndex,
|
||||
type: fromConnectionType as CanvasConnectionPortType,
|
||||
type: fromConnectionType,
|
||||
},
|
||||
target: {
|
||||
index: toIndex,
|
||||
type: toConnectionType as CanvasConnectionPortType,
|
||||
type: toConnectionType,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -71,6 +94,21 @@ export function parseCanvasConnectionHandleString(handle: string | null | undefi
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width and height of a connection
|
||||
*
|
||||
* @TODO See whether this is actually needed or just a legacy jsPlumb check
|
||||
*/
|
||||
export function getVueFlowConnectorLengths(connection: VueFlowConnection): [number, number] {
|
||||
const connectionId = createCanvasConnectionId(connection);
|
||||
const edgeRef = document.getElementById(connectionId);
|
||||
if (!edgeRef) {
|
||||
return [PUSH_NODES_OFFSET, PUSH_NODES_OFFSET];
|
||||
}
|
||||
|
||||
return [edgeRef.clientWidth, edgeRef.clientHeight];
|
||||
}
|
||||
|
||||
export function createCanvasConnectionHandleString({
|
||||
mode,
|
||||
type = NodeConnectionType.Main,
|
||||
|
@ -83,6 +121,10 @@ export function createCanvasConnectionHandleString({
|
|||
return `${mode}/${type}/${index}`;
|
||||
}
|
||||
|
||||
export function createCanvasConnectionId(connection: Connection) {
|
||||
return `[${connection.source}/${connection.sourceHandle}][${connection.target}/${connection.targetHandle}]`;
|
||||
}
|
||||
|
||||
export function mapCanvasConnectionToLegacyConnection(
|
||||
sourceNode: INodeUi,
|
||||
targetNode: INodeUi,
|
||||
|
|
|
@ -8,6 +8,7 @@ import { nodeConnectionTypes } from 'n8n-workflow';
|
|||
import type { IExecutionResponse, ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||
import type { jsPlumbDOMElement } from '@jsplumb/browser-ui';
|
||||
import type { Connection } from '@jsplumb/core';
|
||||
import type { Connection as VueFlowConnection } from '@vue-flow/core';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
import type { CanvasConnectionMode } from '@/types';
|
||||
import { canvasConnectionModes } from '@/types';
|
||||
|
@ -66,7 +67,7 @@ export function isDateObject(date: unknown): date is Date {
|
|||
}
|
||||
|
||||
export function isValidNodeConnectionType(
|
||||
connectionType: string,
|
||||
connectionType: string | undefined,
|
||||
): connectionType is NodeConnectionType {
|
||||
return nodeConnectionTypes.includes(connectionType as NodeConnectionType);
|
||||
}
|
||||
|
@ -75,6 +76,15 @@ export function isValidCanvasConnectionMode(mode: string): mode is CanvasConnect
|
|||
return canvasConnectionModes.includes(mode as CanvasConnectionMode);
|
||||
}
|
||||
|
||||
export function isVueFlowConnection(connection: object): connection is VueFlowConnection {
|
||||
return (
|
||||
'source' in connection &&
|
||||
'target' in connection &&
|
||||
'sourceHandle' in connection &&
|
||||
'targetHandle' in connection
|
||||
);
|
||||
}
|
||||
|
||||
export function isTriggerPanelObject(
|
||||
triggerPanel: INodeTypeDescription['triggerPanel'],
|
||||
): triggerPanel is TriggerPanelDefinition {
|
||||
|
|
|
@ -80,7 +80,6 @@ import { useTagsStore } from '@/stores/tags.store';
|
|||
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { getNodeViewTab } from '@/utils/canvasUtils';
|
||||
import { parseCanvasConnectionHandleString } from '@/utils/canvasUtilsV2';
|
||||
import CanvasStopCurrentExecutionButton from '@/components/canvas/elements/buttons/CanvasStopCurrentExecutionButton.vue';
|
||||
import CanvasStopWaitingForWebhookButton from '@/components/canvas/elements/buttons/CanvasStopWaitingForWebhookButton.vue';
|
||||
import CanvasClearExecutionDataButton from '@/components/canvas/elements/buttons/CanvasClearExecutionDataButton.vue';
|
||||
|
@ -529,14 +528,13 @@ function onCreateConnection(connection: Connection) {
|
|||
}
|
||||
|
||||
function onCreateConnectionCancelled(event: ConnectStartEvent) {
|
||||
const { type, index } = parseCanvasConnectionHandleString(event.handleId);
|
||||
setTimeout(() => {
|
||||
nodeCreatorStore.openNodeCreatorForConnectingNode({
|
||||
index,
|
||||
endpointUuid: event.handleId,
|
||||
connection: {
|
||||
source: event.nodeId,
|
||||
sourceHandle: event.handleId,
|
||||
},
|
||||
eventSource: NODE_CREATOR_OPEN_SOURCES.NODE_CONNECTION_DROP,
|
||||
outputType: type,
|
||||
sourceId: event.nodeId,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -574,6 +572,8 @@ async function onAddNodesAndConnections(
|
|||
await addConnections(connections, {
|
||||
offsetIndex: editableWorkflow.value.nodes.length - nodes.length,
|
||||
});
|
||||
|
||||
uiStore.lastSelectedNodeConnection = null;
|
||||
}
|
||||
|
||||
async function onSwitchActiveNode(nodeName: string) {
|
||||
|
@ -588,6 +588,13 @@ function onOpenNodeCreator(options: ToggleNodeCreatorOptions) {
|
|||
nodeCreatorStore.openNodeCreator(options);
|
||||
}
|
||||
|
||||
function onClickConnectionAdd(connection: Connection) {
|
||||
nodeCreatorStore.openNodeCreatorForConnectingNode({
|
||||
connection,
|
||||
eventSource: NODE_CREATOR_OPEN_SOURCES.NODE_CONNECTION_ACTION,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executions
|
||||
*/
|
||||
|
@ -1079,6 +1086,7 @@ onBeforeUnmount(() => {
|
|||
@create:connection="onCreateConnection"
|
||||
@create:connection:cancelled="onCreateConnectionCancelled"
|
||||
@delete:connection="onDeleteConnection"
|
||||
@click:connection:add="onClickConnectionAdd"
|
||||
@click:pane="onClickPane"
|
||||
>
|
||||
<div :class="$style.executionButtons">
|
||||
|
|
Loading…
Reference in a new issue