Skip to content

Commit 88beaec

Browse files
committed
chore(docs): update drag and drop example
1 parent be57e51 commit 88beaec

File tree

7 files changed

+162
-69
lines changed

7 files changed

+162
-69
lines changed

docs/examples/dnd/App.vue

Lines changed: 17 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,31 @@
11
<script setup>
2+
import { ref } from 'vue'
23
import { VueFlow, useVueFlow } from '@vue-flow/core'
3-
import { nextTick, watch } from 'vue'
4+
import DropzoneBackground from './DropzoneBackground.vue'
45
import Sidebar from './Sidebar.vue'
6+
import useDragAndDrop from './useDnD'
57
6-
let id = 0
7-
function getId() {
8-
return `dndnode_${id++}`
9-
}
8+
const { onConnect, addEdges } = useVueFlow()
109
11-
const { findNode, onConnect, addEdges, addNodes, project, vueFlowRef } = useVueFlow({
12-
nodes: [
13-
{
14-
id: '1',
15-
type: 'input',
16-
label: 'input node',
17-
position: { x: 250, y: 25 },
18-
},
19-
],
20-
})
10+
const { onDragOver, onDrop, onDragLeave, isDragging } = useDragAndDrop()
2111
22-
function onDragOver(event) {
23-
event.preventDefault()
12+
const nodes = ref([])
2413
25-
if (event.dataTransfer) {
26-
event.dataTransfer.dropEffect = 'move'
27-
}
28-
}
29-
30-
onConnect((params) => addEdges(params))
31-
32-
function onDrop(event) {
33-
const type = event.dataTransfer?.getData('application/vueflow')
34-
35-
const { left, top } = vueFlowRef.value.getBoundingClientRect()
36-
37-
const position = project({
38-
x: event.clientX - left,
39-
y: event.clientY - top,
40-
})
41-
42-
const newNode = {
43-
id: getId(),
44-
type,
45-
position,
46-
label: `${type} node`,
47-
}
48-
49-
addNodes([newNode])
50-
51-
// align node position after drop, so it's centered to the mouse
52-
nextTick(() => {
53-
const node = findNode(newNode.id)
54-
const stop = watch(
55-
() => node.dimensions,
56-
(dimensions) => {
57-
if (dimensions.width > 0 && dimensions.height > 0) {
58-
node.position = { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 }
59-
stop()
60-
}
61-
},
62-
{ deep: true, flush: 'post' },
63-
)
64-
})
65-
}
14+
onConnect(addEdges)
6615
</script>
6716

6817
<template>
6918
<div class="dndflow" @drop="onDrop">
70-
<VueFlow @dragover="onDragOver" />
19+
<VueFlow :nodes="nodes" @dragover="onDragOver" @dragleave="onDragLeave">
20+
<DropzoneBackground
21+
:style="{
22+
backgroundColor: isDragging ? 'rgba(0, 0, 0, 0.1)' : 'transparent',
23+
borderColor: isDragging ? 'rgba(0, 0, 0, 0.2)' : 'transparent',
24+
transition: 'background-color 0.2s, border-color 0.2s',
25+
}"
26+
>
27+
</DropzoneBackground>
28+
</VueFlow>
7129

7230
<Sidebar />
7331
</div>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script lang="ts" setup>
2+
import { Background } from '@vue-flow/background'
3+
</script>
4+
5+
<template>
6+
<div style="height: 100%; width: 100%">
7+
<Background :size="2" :gap="20" pattern-color="#BDBDBD">
8+
<slot />
9+
</Background>
10+
</div>
11+
</template>

docs/examples/dnd/Sidebar.vue

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
<script setup>
2-
function onDragStart(event, nodeType) {
3-
if (event.dataTransfer) {
4-
event.dataTransfer.setData('application/vueflow', nodeType)
5-
event.dataTransfer.effectAllowed = 'move'
6-
}
7-
}
2+
import useDragAndDrop from './useDnD'
3+
4+
const { onDragStart } = useDragAndDrop()
85
</script>
96

107
<template>

docs/examples/dnd/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export { default as DndApp } from './App.vue?raw'
22
export { default as DndSidebar } from './Sidebar.vue?raw'
3+
export { default as DndBackground } from './DropzoneBackground.vue?raw'
34
export { default as DndCSS } from './style.css?inline'
5+
export { default as DndScript } from './useDnD.js?raw'

docs/examples/dnd/useDnD.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { useVueFlow } from '@vue-flow/core'
2+
import { ref, watch } from 'vue'
3+
4+
let id = 0
5+
6+
/**
7+
* @returns {string} - A unique id.
8+
*/
9+
function getId() {
10+
return `dndnode_${id++}`
11+
}
12+
13+
/**
14+
* In a real world scenario you'd want to avoid creating refs in a global scope like this as they might not be cleaned up properly.
15+
* @type {{draggedType: Ref<string|null>, isDragOver: Ref<boolean>, isDragging: Ref<boolean>}}
16+
*/
17+
const state = {
18+
/**
19+
* The type of the node being dragged.
20+
*/
21+
draggedType: ref(null),
22+
isDragOver: ref(false),
23+
isDragging: ref(false),
24+
}
25+
26+
export default function useDragAndDrop() {
27+
const { draggedType, isDragOver, isDragging } = state
28+
29+
const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow()
30+
31+
watch(isDragging, (dragging) => {
32+
document.body.style.userSelect = dragging ? 'none' : ''
33+
})
34+
35+
function onDragStart(event, type) {
36+
if (event.dataTransfer) {
37+
event.dataTransfer.setData('application/vueflow', type)
38+
event.dataTransfer.effectAllowed = 'move'
39+
}
40+
41+
draggedType.value = type
42+
isDragging.value = true
43+
44+
document.addEventListener('drop', onDragEnd)
45+
}
46+
47+
/**
48+
* Handles the drag over event.
49+
*
50+
* @param {DragEvent} event
51+
*/
52+
function onDragOver(event) {
53+
event.preventDefault()
54+
55+
if (draggedType.value) {
56+
isDragOver.value = true
57+
58+
if (event.dataTransfer) {
59+
event.dataTransfer.dropEffect = 'move'
60+
}
61+
}
62+
}
63+
64+
function onDragLeave() {
65+
isDragOver.value = false
66+
}
67+
68+
function onDragEnd() {
69+
isDragging.value = false
70+
isDragOver.value = false
71+
draggedType.value = null
72+
document.removeEventListener('drop', onDragEnd)
73+
}
74+
75+
/**
76+
* Handles the drop event.
77+
*
78+
* @param {DragEvent} event
79+
*/
80+
function onDrop(event) {
81+
const position = screenToFlowCoordinate({
82+
x: event.clientX,
83+
y: event.clientY,
84+
})
85+
86+
const nodeId = getId()
87+
88+
const newNode = {
89+
id: nodeId,
90+
type: draggedType.value,
91+
position,
92+
label: `[${nodeId}]`,
93+
}
94+
95+
/**
96+
* Align node position after drop, so it's centered to the mouse
97+
*
98+
* We can hook into events even in a callback, and we can remove the event listener after it's been called.
99+
*/
100+
const { off } = onNodesInitialized(() => {
101+
updateNode(nodeId, (node) => ({
102+
position: { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 },
103+
}))
104+
105+
off()
106+
})
107+
108+
addNodes(newNode)
109+
}
110+
111+
return {
112+
draggedType,
113+
isDragOver,
114+
isDragging,
115+
onDragStart,
116+
onDragLeave,
117+
onDragOver,
118+
onDrop,
119+
}
120+
}

docs/examples/edges/App.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<script setup>
2+
import { h, ref } from 'vue'
23
import { Background } from '@vue-flow/background'
34
import { MarkerType, VueFlow } from '@vue-flow/core'
4-
import { h, ref } from 'vue'
55
import CustomEdge from './CustomEdge.vue'
66
import CustomEdge2 from './CustomEdge2.vue'
77
import CustomEdgeLabel from './CustomEdgeLabel.vue'
88
9-
const elements = ref([
9+
const nodes = ref([
1010
{ id: '1', type: 'input', label: 'Start', position: { x: 50, y: 0 }, style: { borderColor: '#10b981' } },
1111
{ id: '2', label: 'Node 2', position: { x: 150, y: 100 } },
1212
{ id: '2a', label: 'Node 2a', position: { x: 0, y: 180 } },
@@ -18,6 +18,9 @@ const elements = ref([
1818
{ id: '7', type: 'output', label: 'Output 7', position: { x: 50, y: 600 } },
1919
{ id: '8', type: 'output', label: 'Output 8', position: { x: 350, y: 600 } },
2020
{ id: '9', type: 'output', label: 'Output 9', position: { x: 550, y: 400 } },
21+
])
22+
23+
const edges = ref([
2124
{ id: 'e1-2', source: '1', target: '2', label: 'bezier edge (default)', class: 'normal-edge' },
2225
{ id: 'e2-2a', source: '2', target: '2a', type: 'smoothstep', label: 'smoothstep edge' },
2326
{ id: 'e2-3', source: '2', target: '3', type: 'step', label: 'step edge' },
@@ -61,7 +64,7 @@ const elements = ref([
6164
</script>
6265

6366
<template>
64-
<VueFlow v-model="elements" :fit-view-on-init="true" :snap-to-grid="true">
67+
<VueFlow :nodes="nodes" :edges="edges" fit-view-on-init>
6568
<template #edge-custom="props">
6669
<CustomEdge v-bind="props" />
6770
</template>

docs/examples/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { UpdateEdgeApp } from './update-edge'
88
import { UpdateNodeApp, UpdateNodeCSS } from './update-node'
99
import { ValidationApp, ValidationCSS, ValidationCustomInput, ValidationCustomNode } from './validation'
1010
import { SaveRestoreApp, SaveRestoreCSS, SaveRestoreControls } from './save-restore'
11-
import { DndApp, DndCSS, DndSidebar } from './dnd'
11+
import { DndApp, DndBackground, DndCSS, DndScript, DndSidebar } from './dnd'
1212
import { EmptyApp } from './empty'
1313
import { HiddenApp } from './hidden'
1414
import { InteractionApp, InteractionCSS, InteractionControls } from './interaction'
@@ -75,7 +75,9 @@ export const exampleImports = {
7575
dnd: {
7676
'App.vue': DndApp,
7777
'Sidebar.vue': DndSidebar,
78+
'DropzoneBackground.vue': DndBackground,
7879
'style.css': DndCSS,
80+
'useDnD.js': DndScript,
7981
},
8082
empty: {
8183
'App.vue': EmptyApp,

0 commit comments

Comments
 (0)