Vue-Flow์์ Node๋ฅผ Drag & Dropํ์ฌ ํ๋ฉด์ ์๋ก์ด Node๋ฅผ ์์ฑํด ๋ณธ๋ค.
๐ Vue Flow - The customizable Vue3 Flowchart Library
์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ข์ธก Drawer์์ โEXAM NODE๋ฅผ Drag & Drop ํ์ฌ ์ฐ์ธก์
Form` ์ ๊ฐ๋ Node๋ฅผ ํ๋ ์์ฑํด ๋ณด์.
<aside> ๐ก Drawer์ ์๋ Node์ ์์ฑ๋๋ Node์ ํํ ๋ฐ ์๊น์๋ ๋์ผํ ์๋ ์์ผ๋, ๋ค๋ฅธ ๋ชจ์ต์ผ๋ก ์์ฑ๋๊ฒ ํ ์ ์๋ค.
</aside>
์ฐ๋ฆฌ๋ ํด๋ผ์ฐ๋ ์๋น์ค์ ํด๋นํ๋ ๋ ธ๋๋ฅผ ์์ฑํด์ผ ํ๋ฏ๋ก, ๊ฐ์ข ์ ๋ ฅ์ ๋ฐ์์ผ ํ๊ธฐ ๋๋ฌธ์ Custom Node๋ฅผ ์์ฑํ ๊ฒ์ด๋ค.
<script setup>
function onDragStart(event, nodeType) {
if (event.dataTransfer) {
event.dataTransfer.setData('application/vueflow', nodeType)
event.dataTransfer.effectAllowed = 'move'
}
}
</script>
<template>
<v-navigation-drawer
width="244"
>
<div class="nodes">
<div class="vue-flow__node-default" :draggable="true"
@dragstart="onDragStart($event, 'exam1')">Exam Node</div>
<div class="vue-flow__node-default" :draggable="true"
@dragstart="onDragStart($event, 'default')">Default Node</div>
<div class="vue-flow__node-output" :draggable="true"
@dragstart="onDragStart($event, 'output')">Output Node</div>
</div>
</v-navigation-drawer>
</template>
function onDragStart()
Drawer์ ์๋ ๊ฐ Node์ ๋๋๊ทธ ์ด๋ฒคํธ(@dragstart
) ํธ๋ค๋ฌ์ด๋ค. 1) ์ด๋ฒคํธ์, 2) ๋
ธ๋ ํ์
์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ ๋ฌํ๋ค.
<aside>
๐ก ์ฌ๊ธฐ์๋ ์ฒซ ๋ฒ์งธ ๋
ธ๋์ ๋๋๊ทธ ์์ ํธ๋ค๋ฌ์ nodeType์ exam1
์ผ๋ก ์ ๋ฌํ๋ค.
</aside>
๊ฐ ๋ ธ๋
class
์ ๊ฐ ๊ฐ๋ค (vue-flow__node-{type}
)์ ์ง์ ํ๋ค. ์ง๊ธ๊น์ง ํ์
ํ ๋ฐ๋ก๋ default
, input
, output
์ผ๋ก ์ง์ ํ๋ฉด ๊ธฐ๋ณธ ์ ๊ณต UI๋ก ๋ณด์ธ๋ค. type
์ ASDF
๋ก ์ง์ ํ๋ฉด CSS ํ์ผ์ ASDF
์ ํด๋นํ๋ ์คํ์ผ์ ์ ๊ณตํด์ผ ํ๋ค.
<script setup>
import ExamNode1 from './components/ExamNode1.vue';
import AppBar from './layouts/default/AppBar.vue'
import LeftDrawer from './layouts/default/LeftDrawer.vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import { nextTick, watch } from 'vue'
let id = 0
function getId() {
return `dndnode_${id++}`
}
const { findNode, onConnect, addEdges, addNodes, project, vueFlowRef } = useVueFlow({
nodes: [],
})
function onDragOver(event) {
event.preventDefault()
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move'
}
}
onConnect((params) => addEdges(params))
function onDrop(event) {
const type = event.dataTransfer?.getData('application/vueflow')
console.log(type)
const { left, top } = vueFlowRef.value.getBoundingClientRect()
const position = project({
x: event.clientX - left,
y: event.clientY - top,
})
const newNode = {
id: getId(),
type,
position,
label: `${type} node`,
info: {"foo": "bar"},
}
addNodes([newNode])
// align node position after drop, so it's centered to the mouse
nextTick(() => {
const node = findNode(newNode.id)
const stop = watch(
() => node.dimensions,
(dimensions) => {
if (dimensions.width > 0 && dimensions.height > 0) {
node.position = { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 }
stop()
}
},
{ deep: true, flush: 'post' },
)
})
}
</script>
useVueFlow()
- ์ง๊ธ๊น์ง ํ์
ํ ๋ฐ๋ก๋, nodes
๋ค์ ๋ํ ๊ฐ์ข
ํจ์๋ฅผ ๋ฆฌํดํ๋ ๊ฒ ๊ฐ๋ค.onDragOver()
- ๋๋๊ทธ๊ฐ ๋๋ฌ์ ๋ (@onDragOver
) ์ด๋ฒคํธ ํธ๋ค๋ฌ.onDrop()
- ๋
ธ๋๋ฅผ Drop ํ ๋ (@onDrop
) ์ด๋ฒคํธ ํธ๋ค๋ฌ.
type
- ์ Drawer์ onDragStart()
ํจ์์์ ์ ๋ฌ๋ nodeType์ด๋ค.newNode
, addNodes()
- ์ ๋ฌ๋ฐ์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋
ธ๋๋ฅผ ์์ฑํด ์ถ๊ฐํ๋ค.nextTick()
- ์์น ์ง์ ๊ด๋ จ ๋ก์ง ๊ฐ๋ค. ์ถํ์ ๋ ์์ธํ ์์๋ณธ๋ค.