Skip to content

Commit

Permalink
feat(editor): Add remove node and connections functionality to canvas…
Browse files Browse the repository at this point in the history
… v2 (#9602)
  • Loading branch information
alexgrozav authored Jun 4, 2024
1 parent 202c91e commit f6a466c
Show file tree
Hide file tree
Showing 13 changed files with 875 additions and 124 deletions.
6 changes: 3 additions & 3 deletions packages/editor-ui/src/__tests__/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ export function createTestWorkflow(options: {
} as IWorkflowDb;
}

export function createTestNode(
node: Partial<INode> & { name: INode['name']; type: INode['type'] },
): INode {
export function createTestNode(node: Partial<INode> = {}): INode {
return {
id: uuid(),
name: 'Node',
type: 'n8n-nodes-base.test',
typeVersion: 1,
position: [0, 0] as [number, number],
parameters: {},
Expand Down
37 changes: 32 additions & 5 deletions packages/editor-ui/src/components/canvas/Canvas.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<script lang="ts" setup>
import type { CanvasConnection, CanvasElement } from '@/types';
import type { NodeDragEvent, Connection } from '@vue-flow/core';
import { VueFlow, PanelPosition } from '@vue-flow/core';
import { useVueFlow, VueFlow, PanelPosition } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import { Controls } from '@vue-flow/controls';
import { MiniMap } from '@vue-flow/minimap';
import CanvasNode from './elements/nodes/CanvasNode.vue';
import CanvasEdge from './elements/edges/CanvasEdge.vue';
import { useCssModule } from 'vue';
import { onMounted, onUnmounted, useCssModule } from 'vue';
const $style = useCssModule();
const emit = defineEmits<{
'update:modelValue': [elements: CanvasElement[]];
'update:node:position': [id: string, position: { x: number; y: number }];
'delete:node': [id: string];
'delete:connection': [connection: Connection];
'create:connection': [connection: Connection];
}>();
withDefaults(
const props = withDefaults(
defineProps<{
id?: string;
elements: CanvasElement[];
Expand All @@ -32,15 +34,40 @@ withDefaults(
},
);
const { getSelectedEdges, getSelectedNodes } = useVueFlow({ id: props.id });
onMounted(() => {
document.addEventListener('keydown', onKeyDown);
});
onUnmounted(() => {
document.removeEventListener('keydown', onKeyDown);
});
function onNodeDragStop(e: NodeDragEvent) {
e.nodes.forEach((node) => {
emit('update:node:position', node.id, node.position);
});
}
function onDeleteNode(id: string) {
emit('delete:node', id);
}
function onDeleteConnection(connection: Connection) {
emit('delete:connection', connection);
}
function onConnect(...args: unknown[]) {
emit('create:connection', args[0] as Connection);
}
function onKeyDown(e: KeyboardEvent) {
if (e.key === 'Delete') {
getSelectedEdges.value.forEach(onDeleteConnection);
getSelectedNodes.value.forEach(({ id }) => onDeleteNode(id));
}
}
</script>

<template>
Expand All @@ -58,11 +85,11 @@ function onConnect(...args: unknown[]) {
@connect="onConnect"
>
<template #node-canvas-node="canvasNodeProps">
<CanvasNode v-bind="canvasNodeProps" />
<CanvasNode v-bind="canvasNodeProps" @delete="onDeleteNode" />
</template>

<template #edge-canvas-edge="canvasEdgeProps">
<CanvasEdge v-bind="canvasEdgeProps" />
<CanvasEdge v-bind="canvasEdgeProps" @delete="onDeleteConnection" />
</template>

<Background data-test-id="canvas-background" pattern-color="#aaa" :gap="16" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
<script lang="ts" setup>
import type { EdgeProps } from '@vue-flow/core';
import { BaseEdge, getBezierPath } from '@vue-flow/core';
import { computed } from 'vue';
/* eslint-disable vue/no-multiple-template-root */
import type { Connection, EdgeProps } from '@vue-flow/core';
import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@vue-flow/core';
import { computed, useCssModule } from 'vue';
import { useI18n } from '@/composables/useI18n';
const emit = defineEmits<{
delete: [connection: Connection];
}>();
const props = defineProps<EdgeProps>();
const i18n = useI18n();
const $style = useCssModule();
const edgeStyle = computed(() => ({
strokeWidth: 2,
...props.style,
}));
const edgeLabelStyle = computed(() => ({
transform: `translate(-50%, -50%) translate(${path.value[1]}px,${path.value[2]}px)`,
}));
const path = computed(() =>
getBezierPath({
sourceX: props.sourceX,
Expand All @@ -20,6 +33,17 @@ const path = computed(() =>
targetPosition: props.targetPosition,
}),
);
const connection = computed<Connection>(() => ({
source: props.source,
target: props.target,
sourceHandle: props.sourceHandleId,
targetHandle: props.targetHandleId,
}));
function onDelete() {
emit('delete', connection.value);
}
</script>

<template>
Expand All @@ -37,4 +61,23 @@ const path = computed(() =>
:label-bg-padding="[2, 4]"
:label-bg-border-radius="2"
/>
<EdgeLabelRenderer>
<div :class="[$style.edgeToolbar, 'nodrag', 'nopan']" :style="edgeLabelStyle">
<N8nIconButton
data-test-id="delete-connection-button"
type="tertiary"
size="small"
icon="trash"
:title="i18n.baseText('node.delete')"
@click="onDelete"
/>
</div>
</EdgeLabelRenderer>
</template>

<style lang="scss" module>
.edgeToolbar {
pointer-events: all;
position: absolute;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import { useNodeConnections } from '@/composables/useNodeConnections';
import { CanvasNodeKey } from '@/constants';
import type { NodeProps } from '@vue-flow/core';
const emit = defineEmits<{
delete: [id: string];
}>();
const props = defineProps<NodeProps<CanvasElementData>>();
const inputs = computed(() => props.data.inputs);
Expand Down Expand Up @@ -89,6 +93,10 @@ provide(CanvasNodeKey, {
selected,
nodeType,
});
function onDelete() {
emit('delete', props.id);
}
</script>

<template>
Expand Down Expand Up @@ -121,6 +129,7 @@ provide(CanvasNodeKey, {
v-if="nodeType"
data-test-id="canvas-node-toolbar"
:class="$style.canvasNodeToolbar"
@delete="onDelete"
/>

<CanvasNodeRenderer v-if="nodeType">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<script setup lang="ts">
import { computed, inject, useCssModule } from 'vue';
import { CanvasNodeKey } from '@/constants';
import { useI18n } from '@/composables/useI18n';
const emit = defineEmits(['delete']);
const $style = useCssModule();
const node = inject(CanvasNodeKey);
const data = computed(() => node?.data.value);
const i18n = useI18n();
const $style = useCssModule();
const data = computed(() => node?.data.value);
// @TODO
const workflowRunning = false;
Expand All @@ -20,8 +24,9 @@ function executeNode() {}
// @TODO
function toggleDisableNode() {}
// @TODO
function deleteNode() {}
function deleteNode() {
emit('delete');
}
// @TODO
function openContextMenu(_e: MouseEvent, _type: string) {}
Expand All @@ -38,7 +43,7 @@ function openContextMenu(_e: MouseEvent, _type: string) {}
size="small"
icon="play"
:disabled="workflowRunning"
:title="$locale.baseText('node.testStep')"
:title="i18n.baseText('node.testStep')"
@click="executeNode"
/>
<N8nIconButton
Expand All @@ -56,7 +61,7 @@ function openContextMenu(_e: MouseEvent, _type: string) {}
size="small"
text
icon="trash"
:title="$locale.baseText('node.delete')"
:title="i18n.baseText('node.delete')"
@click="deleteNode"
/>
<N8nIconButton
Expand Down
Loading

0 comments on commit f6a466c

Please sign in to comment.