feat(editor): Overhaul node insert position computation in new canvas (no-changelog) (#10637)

This commit is contained in:
Alex Grozav 2024-09-03 15:11:44 +03:00 committed by GitHub
parent e5aba60aff
commit 32ce65c1af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 805 additions and 483 deletions

View file

@ -1805,6 +1805,7 @@ export type ToggleNodeCreatorOptions = {
createNodeActive: boolean;
source?: NodeCreatorOpenSource;
nodeCreatorView?: NodeFilterType;
hasAddedNodes?: boolean;
};
export type AppliedThemeOption = 'light' | 'dark';

View file

@ -49,18 +49,18 @@ export const mockNode = ({
}) => mock<INodeUi>({ id, name, type, position, disabled, issues, typeVersion, parameters });
export const mockNodeTypeDescription = ({
name,
name = SET_NODE_TYPE,
version = 1,
credentials = [],
inputs = [NodeConnectionType.Main],
outputs = [NodeConnectionType.Main],
}: {
name: INodeTypeDescription['name'];
name?: INodeTypeDescription['name'];
version?: INodeTypeDescription['version'];
credentials?: INodeTypeDescription['credentials'];
inputs?: INodeTypeDescription['inputs'];
outputs?: INodeTypeDescription['outputs'];
}) =>
} = {}) =>
mock<INodeTypeDescription>({
name,
displayName: name,

View file

@ -23,7 +23,7 @@ const LazyNodeCreator = defineAsyncComponent(
);
const props = withDefaults(defineProps<Props>(), {
createNodeActive: false,
createNodeActive: false, // Determines if the node creator is open
});
const emit = defineEmits<{
@ -88,13 +88,15 @@ function addStickyNote() {
emit('addNodes', getAddedNodesAndConnections([{ type: STICKY_NODE_TYPE, position }]));
}
function closeNodeCreator() {
emit('toggleNodeCreator', { createNodeActive: false });
function closeNodeCreator(hasAddedNodes = false) {
if (props.createNodeActive) {
emit('toggleNodeCreator', { createNodeActive: false, hasAddedNodes });
}
}
function nodeTypeSelected(nodeTypes: string[]) {
emit('addNodes', getAddedNodesAndConnections(nodeTypes.map((type) => ({ type }))));
closeNodeCreator();
closeNodeCreator(true);
}
</script>

View file

@ -101,8 +101,8 @@ watch(
);
// Close node creator when the last view stacks is closed
watch(viewStacksLength, (viewStacksLength) => {
if (viewStacksLength === 0) {
watch(viewStacksLength, (value) => {
if (value === 0) {
emit('closeNodeCreator');
setShowScrim(false);
}

View file

@ -57,7 +57,11 @@ const emit = defineEmits<{
'create:connection:start': [handle: ConnectStartEvent];
'create:connection': [connection: Connection];
'create:connection:end': [connection: Connection, event?: MouseEvent];
'create:connection:cancelled': [handle: ConnectStartEvent, event?: MouseEvent];
'create:connection:cancelled': [
handle: ConnectStartEvent,
position: XYPosition,
event?: MouseEvent,
];
'click:connection:add': [connection: Connection];
'click:pane': [position: XYPosition];
'run:workflow': [];
@ -227,7 +231,7 @@ function onConnectEnd(event?: MouseEvent) {
if (connectedHandle.value) {
emit('create:connection:end', connectedHandle.value, event);
} else if (connectingHandle.value) {
emit('create:connection:cancelled', connectingHandle.value, event);
emit('create:connection:cancelled', connectingHandle.value, getProjectedPosition(event), event);
}
connectedHandle.value = undefined;
@ -291,14 +295,19 @@ function emitWithLastSelectedNode(emitFn: (id: string) => void) {
const defaultZoom = 1;
const zoom = ref(defaultZoom);
function onClickPane(event: MouseEvent) {
function getProjectedPosition(event?: MouseEvent) {
const bounds = viewportRef.value?.getBoundingClientRect() ?? { left: 0, top: 0 };
const position = project({
x: event.offsetX - bounds.left,
y: event.offsetY - bounds.top,
});
const offsetX = event?.clientX ?? 0;
const offsetY = event?.clientY ?? 0;
emit('click:pane', position);
return project({
x: offsetX - bounds.left,
y: offsetY - bounds.top,
});
}
function onClickPane(event: MouseEvent) {
emit('click:pane', getProjectedPosition(event));
}
async function onFitView() {

View file

@ -145,8 +145,8 @@ function openContextMenu(event: MouseEvent) {
*/
&.configuration {
--canvas-node--width: 76px;
--canvas-node--height: 76px;
--canvas-node--width: 80px;
--canvas-node--height: 80px;
background: var(--canvas-node--background, var(--node-type-supplemental-background));
border: var(--canvas-node-border-width) solid

View file

@ -9,7 +9,7 @@ exports[`useCanvasOperations > copyNodes > should copy nodes 1`] = `
"parameters": {},
"id": "1",
"name": "Node 1",
"type": "type",
"type": "n8n-nodes-base.set",
"position": [
40,
40
@ -20,7 +20,7 @@ exports[`useCanvasOperations > copyNodes > should copy nodes 1`] = `
"parameters": {},
"id": "2",
"name": "Node 2",
"type": "type",
"type": "n8n-nodes-base.set",
"position": [
40,
40
@ -44,7 +44,7 @@ exports[`useCanvasOperations > cutNodes > should copy and delete nodes 1`] = `
"parameters": {},
"id": "1",
"name": "Node 1",
"type": "type",
"type": "n8n-nodes-base.set",
"position": [
40,
40
@ -55,7 +55,7 @@ exports[`useCanvasOperations > cutNodes > should copy and delete nodes 1`] = `
"parameters": {},
"id": "2",
"name": "Node 2",
"type": "type",
"type": "n8n-nodes-base.set",
"position": [
40,
40

View file

@ -17,7 +17,7 @@ import { useDataSchema } from '@/composables/useDataSchema';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useI18n } from '@/composables/useI18n';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { usePinnedData, type PinDataSource } from '@/composables/usePinnedData';
import { type PinDataSource, usePinnedData } from '@/composables/usePinnedData';
import { useTelemetry } from '@/composables/useTelemetry';
import { useToast } from '@/composables/useToast';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
@ -65,6 +65,13 @@ import {
parseCanvasConnectionHandleString,
} from '@/utils/canvasUtilsV2';
import * as NodeViewUtils from '@/utils/nodeViewUtils';
import {
CONFIGURABLE_NODE_SIZE,
CONFIGURATION_NODE_SIZE,
DEFAULT_NODE_SIZE,
GRID_SIZE,
PUSH_NODES_OFFSET,
} from '@/utils/nodeViewUtils';
import { isValidNodeConnectionType } from '@/utils/typeGuards';
import type { Connection } from '@vue-flow/core';
import type {
@ -358,6 +365,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
function setNodeSelected(id?: string) {
if (!id) {
uiStore.lastInteractedWithNodeId = null;
uiStore.lastSelectedNode = '';
return;
}
@ -367,6 +375,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
return;
}
uiStore.lastInteractedWithNodeId = id;
uiStore.lastSelectedNode = node.name;
}
@ -834,73 +843,91 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
}
}
function resolveNodePosition(node: INodeUi, nodeTypeDescription: INodeTypeDescription) {
if (node.position) {
return NodeViewUtils.getNewNodePosition(
canvasStore.getNodesWithPlaceholderNode(),
node.position,
);
}
function resolveNodePosition(
node: Omit<INodeUi, 'position'> & { position?: INodeUi['position'] },
nodeTypeDescription: INodeTypeDescription,
) {
let position: XYPosition | undefined = node.position;
let pushOffsets: XYPosition = [40, 40];
// Available when
// - clicking the plus button of a node handle
// - dragging an edge / connection of a node handle
// - selecting a node, adding a node via the node creator
const lastInteractedWithNode = uiStore.lastInteractedWithNode;
// Available when clicking the plus button of a node edge / connection
const lastInteractedWithNodeConnection = uiStore.lastInteractedWithNodeConnection;
// Available when dragging an edge / connection from a node
const lastInteractedWithNodeHandle = uiStore.lastInteractedWithNodeHandle;
const { type: connectionType, index: connectionIndex } = parseCanvasConnectionHandleString(
lastInteractedWithNodeHandle ?? lastInteractedWithNodeConnection?.sourceHandle ?? '',
);
const nodeSize =
connectionType === NodeConnectionType.Main ? DEFAULT_NODE_SIZE : CONFIGURATION_NODE_SIZE;
if (lastInteractedWithNode) {
const lastSelectedNodeTypeDescription = nodeTypesStore.getNodeType(
const lastInteractedWithNodeTypeDescription = nodeTypesStore.getNodeType(
lastInteractedWithNode.type,
lastInteractedWithNode.typeVersion,
);
const lastInteractedWithNodeObject = editableWorkflowObject.value.getNode(
lastInteractedWithNode.name,
);
if (lastInteractedWithNodeConnection) {
shiftDownstreamNodesPosition(lastInteractedWithNode.name, NodeViewUtils.PUSH_NODES_OFFSET, {
trackHistory: true,
});
}
// This position is set in `onMouseUp` when pulling connections
const newNodeInsertPosition = canvasStore.newNodeInsertPosition;
const newNodeInsertPosition = uiStore.lastCancelledConnectionPosition;
if (newNodeInsertPosition) {
canvasStore.newNodeInsertPosition = null;
return NodeViewUtils.getNewNodePosition(workflowsStore.allNodes, [
newNodeInsertPosition[0] + NodeViewUtils.GRID_SIZE,
newNodeInsertPosition[1] - NodeViewUtils.NODE_SIZE / 2,
]);
} else {
let yOffset = 0;
// When pulling / cancelling a connection.
// The new node should be placed at the same position as the mouse up event,
// designated by the `newNodeInsertPosition` value.
// Compute the y offset for the new node based on the number of main outputs of the source node
if (uiStore.lastInteractedWithNodeConnection) {
const sourceNodeType = nodeTypesStore.getNodeType(
lastInteractedWithNode.type,
lastInteractedWithNode.typeVersion,
const xOffset = connectionType === NodeConnectionType.Main ? 0 : -nodeSize[0] / 2;
const yOffset = connectionType === NodeConnectionType.Main ? -nodeSize[1] / 2 : 0;
position = [newNodeInsertPosition[0] + xOffset, newNodeInsertPosition[1] + yOffset];
uiStore.lastCancelledConnectionPosition = null;
} else if (lastInteractedWithNodeTypeDescription) {
// When
// - clicking the plus button of a node handle
// - clicking the plus button of a node edge / connection
// - selecting a node, adding a node via the node creator
let yOffset = 0;
if (lastInteractedWithNodeConnection) {
// When clicking the plus button of a node edge / connection
// Compute the y offset for the new node based on the number of main outputs of the source node
// and shift the downstream nodes accordingly
shiftDownstreamNodesPosition(lastInteractedWithNode.name, PUSH_NODES_OFFSET, {
trackHistory: true,
});
const yOffsetValuesByOutputCount = [
[-nodeSize[1], nodeSize[1]],
[-nodeSize[1] - 2 * GRID_SIZE, 0, nodeSize[1] - 2 * GRID_SIZE],
[
-2 * nodeSize[1] - 2 * GRID_SIZE,
-nodeSize[1],
nodeSize[1],
2 * nodeSize[1] - 2 * GRID_SIZE,
],
];
const lastInteractedWithNodeOutputs = NodeHelpers.getNodeOutputs(
editableWorkflowObject.value,
lastInteractedWithNode,
lastInteractedWithNodeTypeDescription,
);
const lastInteractedWithNodeOutputTypes = NodeHelpers.getConnectionTypes(
lastInteractedWithNodeOutputs,
);
const lastInteractedWithNodeMainOutputs = lastInteractedWithNodeOutputTypes.filter(
(output) => output === NodeConnectionType.Main,
);
if (sourceNodeType) {
const offsets = [
[-100, 100],
[-140, 0, 140],
[-240, -100, 100, 240],
];
const sourceNodeOutputs = NodeHelpers.getNodeOutputs(
editableWorkflowObject.value,
lastInteractedWithNode,
sourceNodeType,
);
const sourceNodeOutputTypes = NodeHelpers.getConnectionTypes(sourceNodeOutputs);
const sourceNodeOutputMainOutputs = sourceNodeOutputTypes.filter(
(output) => output === NodeConnectionType.Main,
);
if (sourceNodeOutputMainOutputs.length > 1) {
const { index: sourceOutputIndex } = parseCanvasConnectionHandleString(
uiStore.lastInteractedWithNodeConnection.sourceHandle,
);
const offset = offsets[sourceNodeOutputMainOutputs.length - 2];
yOffset = offset[sourceOutputIndex];
}
if (lastInteractedWithNodeMainOutputs.length > 1) {
const yOffsetValues =
yOffsetValuesByOutputCount[lastInteractedWithNodeMainOutputs.length - 2];
yOffset = yOffsetValues[connectionIndex];
}
}
@ -913,80 +940,96 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
// with only "main" outputs.
outputs = NodeHelpers.getNodeOutputs(
editableWorkflowObject.value,
node,
node as INode,
nodeTypeDescription,
);
} catch (e) {}
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
const lastInteractedWithNodeObject = editableWorkflowObject.value.getNode(
lastInteractedWithNode.name,
);
// If node has only scoped outputs, position it below the last selected node
if (lastSelectedNodeTypeDescription) {
pushOffsets = [100, 0];
if (
outputTypes.length > 0 &&
outputTypes.every((outputName) => outputName !== NodeConnectionType.Main) &&
lastInteractedWithNodeObject
) {
// When the added node has only non-main outputs (configuration nodes)
// We want to place the new node directly below the last interacted with node.
const lastInteractedWithNodeInputs = NodeHelpers.getNodeInputs(
editableWorkflowObject.value,
lastInteractedWithNodeObject,
lastInteractedWithNodeTypeDescription,
);
const lastInteractedWithNodeInputTypes = NodeHelpers.getConnectionTypes(
lastInteractedWithNodeInputs,
);
const lastInteractedWithNodeScopedInputTypes = (
lastInteractedWithNodeInputTypes || []
).filter((input) => input !== NodeConnectionType.Main);
const scopedConnectionIndex = lastInteractedWithNodeScopedInputTypes.findIndex(
(inputType) => outputs[0] === inputType,
);
const lastInteractedWithNodeWidthDivisions = Math.max(
lastInteractedWithNodeScopedInputTypes.length + 1,
1,
);
position = [
lastInteractedWithNode.position[0] +
(CONFIGURABLE_NODE_SIZE[0] / lastInteractedWithNodeWidthDivisions) *
(scopedConnectionIndex + 1) -
nodeSize[0] / 2,
lastInteractedWithNode.position[1] + PUSH_NODES_OFFSET,
];
} else {
// When the node has only main outputs, mixed outputs, or no outputs at all
// We want to place the new node directly to the right of the last interacted with node.
const lastInteractedWithNodeInputs = NodeHelpers.getNodeInputs(
editableWorkflowObject.value,
lastInteractedWithNode,
lastInteractedWithNodeTypeDescription,
);
const lastInteractedWithNodeInputTypes = NodeHelpers.getConnectionTypes(
lastInteractedWithNodeInputs,
);
let pushOffset = PUSH_NODES_OFFSET;
if (
lastInteractedWithNodeObject &&
outputTypes.length > 0 &&
outputTypes.every((outputName) => outputName !== NodeConnectionType.Main)
!!lastInteractedWithNodeInputTypes.find((input) => input !== NodeConnectionType.Main)
) {
const lastSelectedInputs = NodeHelpers.getNodeInputs(
editableWorkflowObject.value,
lastInteractedWithNodeObject,
lastSelectedNodeTypeDescription,
);
const lastSelectedInputTypes = NodeHelpers.getConnectionTypes(lastSelectedInputs);
const scopedConnectionIndex = (lastSelectedInputTypes || [])
.filter((input) => input !== NodeConnectionType.Main)
.findIndex((inputType) => outputs[0] === inputType);
return NodeViewUtils.getNewNodePosition(
workflowsStore.allNodes,
[
lastInteractedWithNode.position[0] +
(NodeViewUtils.NODE_SIZE /
(Math.max(lastSelectedNodeTypeDescription?.inputs?.length ?? 1), 1)) *
scopedConnectionIndex,
lastInteractedWithNode.position[1] + NodeViewUtils.PUSH_NODES_OFFSET,
],
[100, 0],
);
} else {
// Has only main outputs or no outputs at all
const inputs = NodeHelpers.getNodeInputs(
editableWorkflowObject.value,
lastInteractedWithNode,
lastSelectedNodeTypeDescription,
);
const inputsTypes = NodeHelpers.getConnectionTypes(inputs);
let pushOffset = NodeViewUtils.PUSH_NODES_OFFSET;
if (!!inputsTypes.find((input) => input !== NodeConnectionType.Main)) {
// If the node has scoped inputs, push it down a bit more
pushOffset += 150;
}
// If a node is active then add the new node directly after the current one
return NodeViewUtils.getNewNodePosition(
workflowsStore.allNodes,
[
lastInteractedWithNode.position[0] + pushOffset,
lastInteractedWithNode.position[1] + yOffset,
],
[100, 0],
);
// If the node has scoped inputs, push it down a bit more
pushOffset += 140;
}
// If a node is active then add the new node directly after the current one
position = [
lastInteractedWithNode.position[0] + pushOffset,
lastInteractedWithNode.position[1] + yOffset,
];
}
}
}
// If added node is a trigger and it's the first one added to the canvas
// we place it at canvasAddButtonPosition to replace the canvas add button
const position = (
nodeTypesStore.isTriggerNode(node.type) && triggerNodes.value.length === 0
? [0, 0]
: // If no node is active find a free spot
lastClickPosition.value
) as XYPosition;
if (!position) {
if (nodeTypesStore.isTriggerNode(node.type) && triggerNodes.value.length === 0) {
// When added node is a trigger, and it's the first one added to the canvas
// we place it at root to replace the canvas add button
return NodeViewUtils.getNewNodePosition(workflowsStore.allNodes, position);
position = [0, 0];
} else {
// When no position is set, we place the node at the last clicked position
position = lastClickPosition.value;
}
}
return NodeViewUtils.getNewNodePosition(workflowsStore.allNodes, position, pushOffsets);
}
function resolveNodeName(node: INodeUi) {
@ -1219,7 +1262,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
function resetWorkspace() {
// Reset node creator
nodeCreatorStore.openNodeCreator({ createNodeActive: false });
nodeCreatorStore.setNodeCreatorState({ createNodeActive: false });
nodeCreatorStore.setShowScrim(false);
// Make sure that if there is a waiting test-webhook, it gets removed
@ -1380,7 +1423,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
// Create a workflow with the new nodes and connections that we can use
// the rename method
const tempWorkflow: Workflow = workflowHelpers.getWorkflow(createNodes, newConnections);
const tempWorkflow: Workflow = workflowsStore.getWorkflow(createNodes, newConnections);
// Rename all the nodes of which the name changed
for (oldName in nodeNameTable) {
@ -1708,6 +1751,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
requireNodeTypeDescription,
addNodes,
addNode,
resolveNodePosition,
revertAddNode,
updateNodesPosition,
updateNodePosition,
@ -1726,6 +1770,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
copyNodes,
cutNodes,
duplicateNodes,
getNodesToSave,
revertDeleteNode,
addConnections,
createConnection,

View file

@ -428,6 +428,7 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, () => {
// #endregion
return {
state,
getCredentialOwnerName,
getCredentialsByType,
getCredentialById,

View file

@ -90,7 +90,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
setTimeout(() => {
if (creatorView) {
openNodeCreator({
setNodeCreatorState({
createNodeActive: true,
nodeCreatorView: creatorView,
});
@ -110,7 +110,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
});
}
function openNodeCreator({
function setNodeCreatorState({
source,
createNodeActive,
nodeCreatorView,
@ -200,7 +200,6 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
uiStore.lastSelectedNode = sourceNode.name;
uiStore.lastSelectedNodeEndpointUuid = connection.sourceHandle ?? null;
uiStore.lastSelectedNodeOutputIndex = index;
// canvasStore.newNodeInsertPosition = null;
if (isVueFlowConnection(connection)) {
uiStore.lastInteractedWithNodeConnection = connection;
@ -208,7 +207,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
uiStore.lastInteractedWithNodeHandle = connection.sourceHandle ?? null;
uiStore.lastInteractedWithNodeId = sourceNode.id;
openNodeCreator({
setNodeCreatorState({
source: eventSource,
createNodeActive: true,
nodeCreatorView,
@ -231,7 +230,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
ndvStore.activeNodeName = null;
setSelectedView(TRIGGER_NODE_CREATOR_VIEW);
setShowScrim(true);
openNodeCreator({
setNodeCreatorState({
source,
createNodeActive: true,
nodeCreatorView: TRIGGER_NODE_CREATOR_VIEW,
@ -276,7 +275,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, () => {
setOpenSource,
setActions,
setMergeNodes,
openNodeCreator,
setNodeCreatorState,
openSelectiveNodeCreator,
openNodeCreatorForConnectingNode,
openNodeCreatorForTriggerNodes,

View file

@ -200,6 +200,7 @@ export const useUIStore = defineStore(STORES.UI, () => {
const lastInteractedWithNodeConnection = ref<Connection | null>(null);
const lastInteractedWithNodeHandle = ref<string | null>(null);
const lastInteractedWithNodeId = ref<string | null>(null);
const lastCancelledConnectionPosition = ref<XYPosition | null>(null);
const settingsStore = useSettingsStore();
const workflowsStore = useWorkflowsStore();
@ -624,6 +625,7 @@ export const useUIStore = defineStore(STORES.UI, () => {
lastInteractedWithNodeConnection.value = null;
lastInteractedWithNodeHandle.value = null;
lastInteractedWithNodeId.value = null;
lastCancelledConnectionPosition.value = null;
}
return {
@ -652,6 +654,7 @@ export const useUIStore = defineStore(STORES.UI, () => {
lastInteractedWithNodeHandle,
lastInteractedWithNodeId,
lastInteractedWithNode,
lastCancelledConnectionPosition,
nodeViewOffsetPosition,
nodeViewMoveInProgress,
nodeViewInitialized,

View file

@ -103,9 +103,8 @@ export function mapLegacyConnectionToCanvasConnection(
export function parseCanvasConnectionHandleString(handle: string | null | undefined) {
const [mode, type, index] = (handle ?? '').split('/');
const resolvedType = isValidNodeConnectionType(type) ? type : NodeConnectionType.Main;
const resolvedMode = isValidCanvasConnectionMode(mode) ? mode : CanvasConnectionMode.Output;
const resolvedType = isValidNodeConnectionType(type) ? type : NodeConnectionType.Main;
let resolvedIndex = parseInt(index, 10);
if (isNaN(resolvedIndex)) {
resolvedIndex = 0;

View file

@ -38,6 +38,9 @@ const MIN_X_TO_SHOW_OUTPUT_LABEL = 90;
const MIN_Y_TO_SHOW_OUTPUT_LABEL = 100;
export const NODE_SIZE = 100;
export const DEFAULT_NODE_SIZE = [100, 100];
export const CONFIGURATION_NODE_SIZE = [80, 80];
export const CONFIGURABLE_NODE_SIZE = [256, 100];
export const PLACEHOLDER_TRIGGER_NODE_SIZE = 100;
export const DEFAULT_START_POSITION_X = 180;
export const DEFAULT_START_POSITION_Y = 240;

View file

@ -34,7 +34,11 @@ import type {
ToggleNodeCreatorOptions,
XYPosition,
} from '@/Interface';
import type { Connection, ViewportTransform } from '@vue-flow/core';
import type {
Connection,
ViewportTransform,
XYPosition as VueFlowXYPosition,
} from '@vue-flow/core';
import type {
CanvasConnectionCreateData,
CanvasEventBusEvents,
@ -738,12 +742,20 @@ function onRevertCreateConnection({ connection }: { connection: [IConnection, IC
revertCreateConnection(connection);
}
function onCreateConnectionCancelled(event: ConnectStartEvent, mouseEvent?: MouseEvent) {
function onCreateConnectionCancelled(
event: ConnectStartEvent,
position: VueFlowXYPosition,
mouseEvent?: MouseEvent,
) {
const preventDefault = (mouseEvent?.target as HTMLElement).classList?.contains('clickable');
if (preventDefault) {
return;
}
uiStore.lastInteractedWithNodeId = event.nodeId;
uiStore.lastInteractedWithNodeHandle = event.handleId;
uiStore.lastCancelledConnectionPosition = [position.x, position.y];
setTimeout(() => {
nodeCreatorStore.openNodeCreatorForConnectingNode({
connection: {
@ -874,11 +886,15 @@ async function onOpenNodeCreatorForTriggerNodes(source: NodeCreatorOpenSource) {
}
function onOpenNodeCreatorFromCanvas(source: NodeCreatorOpenSource) {
onOpenNodeCreator({ createNodeActive: true, source });
onToggleNodeCreator({ createNodeActive: true, source });
}
function onOpenNodeCreator(options: ToggleNodeCreatorOptions) {
nodeCreatorStore.openNodeCreator(options);
function onToggleNodeCreator(options: ToggleNodeCreatorOptions) {
nodeCreatorStore.setNodeCreatorState(options);
if (!options.createNodeActive && !options.hasAddedNodes) {
uiStore.resetLastInteractedWith();
}
}
function onCreateSticky() {
@ -1378,7 +1394,6 @@ function selectNodes(ids: string[]) {
function onClickPane(position: CanvasNode['position']) {
lastClickPosition.value = [position.x, position.y];
canvasStore.newNodeInsertPosition = [position.x, position.y];
uiStore.isCreateNodeActive = false;
}
@ -1563,7 +1578,7 @@ onDeactivated(() => {
v-if="!isCanvasReadOnly"
:create-node-active="uiStore.isCreateNodeActive"
:node-view-scale="viewportTransform.zoom"
@toggle-node-creator="onOpenNodeCreator"
@toggle-node-creator="onToggleNodeCreator"
@add-nodes="onAddNodesAndConnections"
/>
</Suspense>