mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 19:32:15 +00:00
feat(editor): Add drag and drop from nodes panel (#3123)
* ✨ Added support for drag and drop from nodes main panel. ✨ Added node draggable placeholder. * ✨ Added snapping to grid. Changed how draggable ghost follows the cursor. * 💄 Changed node drag anchor position to be centered. * ✨ Added drag and drop animation. Added event cancellation when dropping node on main panel. * ♻️ Simplified drag and drop code and cleaned up prop-drilling. * 🐛 Added check for nodeTypeName in dataTransfer when draging and dropping nodes. * 🐛 Ensured MS Edge compatibility. MS edge does not send datatransfer in ondragover event. Co-authored-by: Mutasem <mutdmour@gmail.com>
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div :class="{[$style['node-item']]: true, [$style.bordered]: bordered}">
|
||||
<div
|
||||
draggable
|
||||
@dragstart="onDragStart"
|
||||
@dragend="onDragEnd"
|
||||
:class="{[$style['node-item']]: true, [$style.bordered]: bordered}"
|
||||
>
|
||||
<NodeIcon :class="$style['node-icon']" :nodeType="nodeType" />
|
||||
<div>
|
||||
<div :class="$style.details">
|
||||
@@ -11,7 +16,7 @@
|
||||
}}
|
||||
</span>
|
||||
<span :class="$style['trigger-icon']">
|
||||
<TriggerIcon v-if="$options.isTrigger(nodeType)" />
|
||||
<TriggerIcon v-if="isTrigger" />
|
||||
</span>
|
||||
</div>
|
||||
<div :class="$style.description">
|
||||
@@ -21,14 +26,26 @@
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div :class="$style['draggable-data-transfer']" ref="draggableDataTransfer" />
|
||||
<transition name="node-item-transition">
|
||||
<div
|
||||
:class="$style.draggable"
|
||||
:style="draggableStyle"
|
||||
ref="draggable"
|
||||
v-show="dragging"
|
||||
>
|
||||
<NodeIcon class="node-icon" :nodeType="nodeType" :size="40" :shrink="false" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import {getNewNodePosition, NODE_SIZE} from '@/views/canvasHelpers';
|
||||
import Vue from 'vue';
|
||||
import { INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import NodeIcon from '../NodeIcon.vue';
|
||||
import TriggerIcon from '../TriggerIcon.vue';
|
||||
@@ -44,14 +61,73 @@ export default Vue.extend({
|
||||
'nodeType',
|
||||
'bordered',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
dragging: false,
|
||||
draggablePosition: {
|
||||
x: -100,
|
||||
y: -100,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
shortNodeType() {
|
||||
shortNodeType(): string {
|
||||
return this.$locale.shortNodeType(this.nodeType.name);
|
||||
},
|
||||
isTrigger (): boolean {
|
||||
return this.nodeType.group.includes('trigger');
|
||||
},
|
||||
draggableStyle(): { top: string; left: string; } {
|
||||
return {
|
||||
top: `${this.draggablePosition.y}px`,
|
||||
left: `${this.draggablePosition.x}px`,
|
||||
};
|
||||
},
|
||||
},
|
||||
// @ts-ignore
|
||||
isTrigger (nodeType: INodeTypeDescription): boolean {
|
||||
return nodeType.group.includes('trigger');
|
||||
mounted() {
|
||||
/**
|
||||
* Workaround for firefox, that doesn't attach the pageX and pageY coordinates to "ondrag" event.
|
||||
* All browsers attach the correct page coordinates to the "dragover" event.
|
||||
* @bug https://bugzilla.mozilla.org/show_bug.cgi?id=505521
|
||||
*/
|
||||
document.body.addEventListener("dragover", this.onDragOver);
|
||||
},
|
||||
destroyed() {
|
||||
document.body.removeEventListener("dragover", this.onDragOver);
|
||||
},
|
||||
methods: {
|
||||
onDragStart(event: DragEvent): void {
|
||||
const { pageX: x, pageY: y } = event;
|
||||
|
||||
this.$emit('dragstart', event);
|
||||
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
event.dataTransfer.setData('nodeTypeName', this.nodeType.name);
|
||||
event.dataTransfer.setDragImage(this.$refs.draggableDataTransfer as Element, 0, 0);
|
||||
}
|
||||
|
||||
this.dragging = true;
|
||||
this.draggablePosition = { x, y };
|
||||
},
|
||||
onDragOver(event: DragEvent): void {
|
||||
if (!this.dragging || event.pageX === 0 && event.pageY === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [x,y] = getNewNodePosition([], [event.pageX - NODE_SIZE / 2, event.pageY - NODE_SIZE / 2]);
|
||||
|
||||
this.draggablePosition = { x, y };
|
||||
},
|
||||
onDragEnd(event: DragEvent): void {
|
||||
this.$emit('dragend', event);
|
||||
|
||||
this.dragging = false;
|
||||
setTimeout(() => {
|
||||
this.draggablePosition = { x: -100, y: -100 };
|
||||
}, 300);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -100,4 +176,39 @@ export default Vue.extend({
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
opacity: 0.66;
|
||||
border: 2px solid var(--color-foreground-xdark);
|
||||
border-radius: var(--border-radius-large);
|
||||
background-color: var(--color-background-xlight);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.draggable-data-transfer {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.node-item-transition {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition-property: opacity, transform;
|
||||
transition-duration: 300ms;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
|
||||
&-enter,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user