Skip to content

Commit

Permalink
feat(editor): Make new canvas connections go underneath node when loo…
Browse files Browse the repository at this point in the history
…ping backwards (n8n-io#11833)
  • Loading branch information
alexgrozav authored Nov 25, 2024
1 parent 459e6aa commit 91d1bd8
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,15 @@ describe('CanvasConnectionLine', () => {
},
});

const edge = container.querySelector('.vue-flow__edge-path');
const edges = container.querySelectorAll('.vue-flow__edge-path');

expect(edge).toHaveAttribute(
expect(edges[0]).toHaveAttribute(
'd',
'M0 0L 24,0Q 40,0 40,16L 40,124Q 40,140 24,140L1 140L0 140M0 140L-40 140L -124,140Q -140,140 -140,124L -140,-84Q -140,-100 -124,-100L-100 -100',
'M0 0L 24,0Q 40,0 40,16L 40,114Q 40,130 24,130L-10 130L-50 130',
);
});

it('should only avoid obstacles when the edge intersects the nodes ', () => {
const { container } = renderComponent({
props: {
...DEFAULT_PROPS,
sourceX: -72,
sourceY: -290,
sourcePosition: Position.Right,
targetX: -344,
targetY: -30,
targetPosition: Position.Left,
},
});

const edge = container.querySelector('.vue-flow__edge-path');

expect(edge).toHaveAttribute(
expect(edges[1]).toHaveAttribute(
'd',
'M-72 -290L -62,-290Q -52,-290 -52,-280L -52,-176Q -52,-160 -68,-160L -348,-160Q -364,-160 -364,-144L -364,-40Q -364,-30 -354,-30L-344 -30',
'M-50 130L-90 130L -124,130Q -140,130 -140,114L -140,-84Q -140,-100 -124,-100L-100 -100',
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { ConnectionLineProps } from '@vue-flow/core';
import { BaseEdge } from '@vue-flow/core';
import { computed, useCssModule } from 'vue';
import { getCustomPath } from './utils/edgePath';
import { getEdgeRenderData } from './utils';
import { useCanvas } from '@/composables/useCanvas';
import { NodeConnectionType } from 'n8n-workflow';
import { parseCanvasConnectionHandleString } from '@/utils/canvasUtilsV2';
Expand Down Expand Up @@ -32,11 +32,22 @@ const edgeStyle = computed(() => ({
stroke: edgeColor.value,
}));
const path = computed(() => getCustomPath(props, { connectionType: connectionType.value }));
const renderData = computed(() =>
getEdgeRenderData(props, { connectionType: connectionType.value }),
);
const segments = computed(() => renderData.value.segments);
</script>

<template>
<BaseEdge :class="$style.edge" :style="edgeStyle" :path="path[0]" :marker-end="markerEnd" />
<BaseEdge
v-for="segment in segments"
:key="segment[0]"
:class="$style.edge"
:style="edgeStyle"
:path="segment[0]"
:marker-end="markerEnd"
/>
</template>

<style lang="scss" module>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,15 @@ describe('CanvasEdge', () => {
},
});

const edge = container.querySelector('.vue-flow__edge-path');
const edges = container.querySelectorAll('.vue-flow__edge-path');

expect(edge).toHaveAttribute(
expect(edges[0]).toHaveAttribute(
'd',
'M0 0L 24,0Q 40,0 40,16L 40,114Q 40,130 24,130L-10 130L-50 130',
);
expect(edges[1]).toHaveAttribute(
'd',
'M0 0L 24,0Q 40,0 40,16L 40,124Q 40,140 24,140L1 140L0 140M0 140L-40 140L -124,140Q -140,140 -140,124L -140,-84Q -140,-100 -124,-100L-100 -100',
'M-50 130L-90 130L -124,130Q -140,130 -140,114L -140,-84Q -140,-100 -124,-100L-100 -100',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BaseEdge, EdgeLabelRenderer } from '@vue-flow/core';
import { NodeConnectionType } from 'n8n-workflow';
import { computed, useCssModule, toRef } from 'vue';
import CanvasEdgeToolbar from './CanvasEdgeToolbar.vue';
import { getCustomPath } from './utils/edgePath';
import { getEdgeRenderData } from './utils';
const emit = defineEmits<{
add: [connection: Connection];
Expand Down Expand Up @@ -73,9 +73,8 @@ const edgeLabelStyle = computed(() => ({
}));
const edgeToolbarStyle = computed(() => {
const [, labelX, labelY] = path.value;
return {
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
transform: `translate(-50%, -50%) translate(${labelPosition.value[0]}px,${labelPosition.value[1]}px)`,
...(props.hovered ? { zIndex: 1 } : {}),
};
});
Expand All @@ -86,12 +85,16 @@ const edgeToolbarClasses = computed(() => ({
selected: props.selected,
}));
const path = computed(() =>
getCustomPath(props, {
const renderData = computed(() =>
getEdgeRenderData(props, {
connectionType: connectionType.value,
}),
);
const segments = computed(() => renderData.value.segments);
const labelPosition = computed(() => renderData.value.labelPosition);
const connection = computed<Connection>(() => ({
source: props.source,
target: props.target,
Expand All @@ -118,10 +121,12 @@ function onEdgeLabelMouseLeave() {

<template>
<BaseEdge
:id="id"
v-for="(segment, index) in segments"
:id="`${id}-${index}`"
:key="segment[0]"
:class="edgeClasses"
:style="edgeStyle"
:path="path[0]"
:path="segment[0]"
:marker-end="markerEnd"
:interaction-width="40"
/>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { EdgeProps } from '@vue-flow/core';
import { getBezierPath, getSmoothStepPath, Position } from '@vue-flow/core';
import { NodeConnectionType } from 'n8n-workflow';

const EDGE_PADDING_BOTTOM = 130;
const EDGE_PADDING_X = 40;
const EDGE_BORDER_RADIUS = 16;
const HANDLE_SIZE = 20; // Required to avoid connection line glitching when initially interacting with the handle

const isRightOfSourceHandle = (sourceX: number, targetX: number) => sourceX - HANDLE_SIZE > targetX;

export function getEdgeRenderData(
props: Pick<
EdgeProps,
'sourceX' | 'sourceY' | 'sourcePosition' | 'targetX' | 'targetY' | 'targetPosition'
>,
{
connectionType = NodeConnectionType.Main,
}: {
connectionType?: NodeConnectionType;
} = {},
) {
const { targetX, targetY, sourceX, sourceY, sourcePosition, targetPosition } = props;

if (!isRightOfSourceHandle(sourceX, targetX) || connectionType !== NodeConnectionType.Main) {
const segment = getBezierPath(props);
return {
segments: [segment],
labelPosition: [segment[1], segment[2]],
};
}

// Connection is backwards and the source is on the right side
// -> We need to avoid overlapping the source node
const firstSegmentTargetX = (sourceX + targetX) / 2;
const firstSegmentTargetY = sourceY + EDGE_PADDING_BOTTOM;
const firstSegment = getSmoothStepPath({
sourceX,
sourceY,
targetX: firstSegmentTargetX,
targetY: firstSegmentTargetY,
sourcePosition,
targetPosition: Position.Right,
borderRadius: EDGE_BORDER_RADIUS,
offset: EDGE_PADDING_X,
});

const secondSegment = getSmoothStepPath({
sourceX: firstSegmentTargetX,
sourceY: firstSegmentTargetY,
targetX,
targetY,
sourcePosition: Position.Left,
targetPosition,
borderRadius: EDGE_BORDER_RADIUS,
offset: EDGE_PADDING_X,
});

return {
segments: [firstSegment, secondSegment],
labelPosition: [firstSegmentTargetX, firstSegmentTargetY],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './getEdgeRenderData';

0 comments on commit 91d1bd8

Please sign in to comment.