docs(guide): update node page
This commit is contained in:
@@ -37,12 +37,10 @@ $ yarn add @vue-flow/core
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
In Vue Flow, an application structure consists
|
In Vue Flow, an application structure consists of [**nodes**](/typedocs/interfaces/Node)
|
||||||
of [<span class="font-bold">nodes</span>](/typedocs/interfaces/Node)
|
and [**edges**](/typedocs/types/Edge), all of which are categorised as [**elements**](/typedocs/types/Elements).
|
||||||
and [<span class="font-bold">edges</span>](/typedocs/types/Edge), all of which are categorised as
|
|
||||||
[<span class="font-bold">elements</span>](/typedocs/types/Elements).
|
|
||||||
|
|
||||||
<span class="font-bold">Each element requires a unique id.</span>
|
**Each element requires a unique id.**
|
||||||
|
|
||||||
Nodes additionally need an [XY-position](/typedocs/interfaces/XYPosition), while edges require a source and a
|
Nodes additionally need an [XY-position](/typedocs/interfaces/XYPosition), while edges require a source and a
|
||||||
target, both represented by node ids.
|
target, both represented by node ids.
|
||||||
|
|||||||
+533
-246
@@ -1,9 +1,19 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { VueFlow } from '@vue-flow/core';
|
import LogosJavascript from '~icons/logos/javascript';
|
||||||
|
import LogosTypescript from '~icons/logos/typescript-icon';
|
||||||
|
import { VueFlow, Panel } from '@vue-flow/core';
|
||||||
import { Background } from '@vue-flow/background';
|
import { Background } from '@vue-flow/background';
|
||||||
import Check from '~icons/mdi/check';
|
import Check from '~icons/mdi/check';
|
||||||
import Close from '~icons/mdi/close';
|
import Close from '~icons/mdi/close';
|
||||||
import { ref } from 'vue';
|
import { ref, h } from 'vue';
|
||||||
|
|
||||||
|
const ScrollableNode = () => h('div', { class: 'custom-node-container' }, [
|
||||||
|
h('ul', { class: 'nowheel' }, Array.from({ length: 100 }, (_, i) => h('li', { key: i }, `Item ${i}`)))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const InputFieldNode = () => h('div', { class: 'custom-node-container' }, [
|
||||||
|
h('input', { class: 'nodrag', placeholder: 'Type something...' })
|
||||||
|
]);
|
||||||
|
|
||||||
const defaultNode = ref([
|
const defaultNode = ref([
|
||||||
{
|
{
|
||||||
@@ -32,66 +42,96 @@ const outputNode = ref([
|
|||||||
]);
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# Nodes
|
# Introduction to Nodes
|
||||||
|
|
||||||
Nodes are the building blocks of your graph. They represent any sort of data you want to present in your graph.
|
Nodes are the underlying components of your graph.
|
||||||
|
They can be any kind of data you want to visualize in your graph, existing independently and being interconnected
|
||||||
|
through edges to create a data map.
|
||||||
|
|
||||||
They can exist on their own but can be connected to each other with edges to create a map.
|
Remember, every node is unique and thus **requires a unique id** and **an [XY-position](/typedocs/interfaces/XYPosition)**.
|
||||||
|
|
||||||
Each node <span class="font-bold text-blue-500">requires a unique id and
|
For the full list of options available for a node, check out the [Node Interface](/typedocs/interfaces/Node).
|
||||||
an [xy-position](/typedocs/interfaces/XYPosition).</span>
|
|
||||||
|
|
||||||
You can view the full options-list for a node [here](/typedocs/interfaces/Node).
|
## Adding Nodes to the Graph
|
||||||
|
|
||||||
## Usage
|
Nodes are generally created by adding them to the `mode-value` (using `v-model`) or to the `nodes` prop of the Vue Flow component.
|
||||||
|
This can be done dynamically at any point in your component's lifecycle.
|
||||||
|
|
||||||
Generally you create nodes by adding them to the model-value or the nodes prop of the Vue Flow component.
|
:::code-group
|
||||||
|
|
||||||
```vue
|
```vue [<LogosJavascript />]
|
||||||
<script>
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
import { VueFlow } from '@vue-flow/core'
|
import { VueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
export default defineComponent({
|
const elements = ref([
|
||||||
components: { VueFlow },
|
{
|
||||||
data() {
|
id: '1',
|
||||||
return {
|
position: {x: 50, y: 50},
|
||||||
elements: [
|
label: 'Node 1',
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
position: { x: 50, y: 50 },
|
|
||||||
label: 'Node 1',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// Add an element after mount
|
|
||||||
this.elements.push(
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
position: { x: 150, y: 50 },
|
|
||||||
label: 'Node 2',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
elements.value.push({
|
||||||
|
id: '2',
|
||||||
|
label: 'Node 2',
|
||||||
|
position: {x: 150, y: 50},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 300px">
|
<VueFlow v-model="elements"/>
|
||||||
<VueFlow v-model="elements" />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
For more advanced graphs that require more state access you will want to use the useVueFlow composable.
|
```vue [<LogosTypescript />]
|
||||||
[useVueFlow](/typedocs/functions/useVueFlow) will provide the [`addNodes`](/typedocs/interfaces/Actions#addnodes) action,
|
|
||||||
which you can use to add nodes directly to the state.
|
|
||||||
|
|
||||||
This action can be even be used outside the component that is rendering the graph, like a Sidebar or a Toolbar.
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import type { Elements } from '@vue-flow/core'
|
||||||
|
import { VueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
|
const elements = ref<Elements>([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
position: { x: 50, y: 50 },
|
||||||
|
label: 'Node 1',
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
elements.value.push({
|
||||||
|
id: '2',
|
||||||
|
label: 'Node 2',
|
||||||
|
position: { x: 150, y: 50 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VueFlow v-model="elements"/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
If you are working with more complex graphs that necessitate extensive state access, the `useVueFlow` composable should
|
||||||
|
be employed.
|
||||||
|
The [`addNodes`](/typedocs/interfaces/Actions#addnodes) action is available
|
||||||
|
through [useVueFlow](/typedocs/functions/useVueFlow), allowing you to add nodes straight to the state.
|
||||||
|
|
||||||
|
What's more, this action isn't limited to the component rendering the graph; it can be utilized elsewhere, like in a
|
||||||
|
Sidebar or Toolbar.
|
||||||
|
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```vue [<LogosJavascript />]
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
const initialNodes = ref([
|
const initialNodes = ref([
|
||||||
@@ -101,117 +141,295 @@ const initialNodes = ref([
|
|||||||
label: 'Node 1',
|
label: 'Node 1',
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const { addNodes } = useVueFlow({
|
const { addNodes } = useVueFlow()
|
||||||
nodes: initialNodes,
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
function generateRandomNode() {
|
||||||
// Add an element after mount
|
return {
|
||||||
addNodes([
|
id: Math.random().toString(),
|
||||||
{
|
position: { x: Math.random() * 500, y: Math.random() * 500 },
|
||||||
id: '2',
|
label: 'Random Node',
|
||||||
position: { x: 150, y: 50 },
|
}
|
||||||
label: 'Node 2',
|
}
|
||||||
}
|
|
||||||
])
|
function onAddNode() {
|
||||||
})
|
// add a single node to the graph
|
||||||
|
addNodes(generateRandomNode())
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddNodes() {
|
||||||
|
// add multiple nodes to the graph
|
||||||
|
addNodes(Array.from({ length: 10 }, generateRandomNode))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 300px">
|
<VueFlow :nodes="initialNodes" />
|
||||||
<VueFlow />
|
|
||||||
</div>
|
<button type="button" @click="onAddNode">Add a node</button>
|
||||||
|
<button type="button" @click="onAddNodes">Add multiple nodes</button>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Default Node-Types](/typedocs/types/DefaultNodeTypes)
|
```vue [<LogosTypescript />]
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { Node } from '@vue-flow/core'
|
||||||
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
Vue Flow comes with built-in nodes that you can use right out of the box.
|
const initialNodes = ref<Node[]>([
|
||||||
These node types include `default`, `input` and `output`.
|
{
|
||||||
|
id: '1',
|
||||||
|
position: { x: 50, y: 50 },
|
||||||
|
label: 'Node 1',
|
||||||
|
data: {},
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const { addNodes } = useVueFlow()
|
||||||
|
|
||||||
|
function generateRandomNode() {
|
||||||
|
return {
|
||||||
|
id: Math.random().toString(),
|
||||||
|
position: { x: Math.random() * 500, y: Math.random() * 500 },
|
||||||
|
label: 'Random Node',
|
||||||
|
data: {
|
||||||
|
hello: 'world',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddNode() {
|
||||||
|
// add a single node to the graph
|
||||||
|
addNodes(generateRandomNode())
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddNodes() {
|
||||||
|
// add multiple nodes to the graph
|
||||||
|
addNodes(Array.from({ length: 10 }, generateRandomNode))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VueFlow :nodes="initialNodes" />
|
||||||
|
|
||||||
|
<button type="button" @click="onAddNode()">Add a node</button>
|
||||||
|
<button type="button" @click="onAddNodes()">Add multiple nodes</button>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## [Predefined Node-Types](/typedocs/types/DefaultNodeTypes)
|
||||||
|
|
||||||
|
Vue Flow provides several built-in node types that you can leverage immediately.
|
||||||
|
The included node types are `default`, `input`, and `output`.
|
||||||
|
|
||||||
### Default Node
|
### Default Node
|
||||||
|
|
||||||
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded h-50">
|
A default node includes two handles and serves as a branching junction in your map.
|
||||||
<VueFlow v-model="defaultNode">
|
|
||||||
<Background />
|
|
||||||
</VueFlow>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
A default node comes with two handles.
|
You have the freedom to determine the location of handles in the node's definition.
|
||||||
It represents a branching point in your map.
|
|
||||||
|
|
||||||
You can specify the position of handles in the node definition.
|
```ts
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Position } from '@vue-flow/core'
|
||||||
|
|
||||||
```js{5-6}
|
const nodes = ref([
|
||||||
const nodes = [
|
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Node 1',
|
label: 'Default Node',
|
||||||
|
type: 'default', // You can omit this as it's the fallback type
|
||||||
targetHandle: Position.Top, // or Bottom, Left, Right,
|
targetHandle: Position.Top, // or Bottom, Left, Right,
|
||||||
sourceHandle: Position.Right,
|
sourceHandle: Position.Bottom, // or Top, Left, Right,
|
||||||
}
|
}
|
||||||
]
|
])
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
|
||||||
|
<VueFlow v-model="defaultNode">
|
||||||
|
<Background class="rounded-lg" />
|
||||||
|
</VueFlow>
|
||||||
|
</div>
|
||||||
|
|
||||||
### Input Node
|
### Input Node
|
||||||
|
|
||||||
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded h-50">
|
An input node features a single handle, which is by default positioned at the bottom.
|
||||||
|
It represents a starting point of your map.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Position } from '@vue-flow/core'
|
||||||
|
|
||||||
|
const nodes = ref([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Input Node',
|
||||||
|
type: 'input',
|
||||||
|
sourceHandle: Position.Bottom, // or Top, Left, Right,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
|
||||||
<VueFlow v-model="inputNode">
|
<VueFlow v-model="inputNode">
|
||||||
<Background />
|
<Background class="rounded-lg" />
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
An input node has a single handle, located at the bottom by default.
|
|
||||||
It represents a starting point of your map.
|
|
||||||
|
|
||||||
### Output Node
|
### Output Node
|
||||||
|
|
||||||
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded h-50">
|
An output node also possesses a single handle, although it is typically found at the top.
|
||||||
|
This node represents a conclusion point of your map.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Position } from '@vue-flow/core'
|
||||||
|
|
||||||
|
const nodes = ref([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Output Node',
|
||||||
|
type: 'output',
|
||||||
|
targetHandle: Position.Top, // or Bottom, Left, Right,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
|
||||||
<VueFlow v-model="outputNode">
|
<VueFlow v-model="outputNode">
|
||||||
<Background />
|
<Background class="rounded-lg" />
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
An output node has a single handle, located at the top by default.
|
|
||||||
It represents an ending point of your map.
|
|
||||||
|
|
||||||
## Custom Nodes
|
## User-Defined Nodes
|
||||||
|
|
||||||
In addition to the default node types from the previous chapter, you can define any amount of custom node-types.
|
On top of the default node types mentioned earlier, you can create as many custom node-types as you need.
|
||||||
Node-types are inferred from your node's definition.
|
Node-types are determined from your nodes' definitions.
|
||||||
|
|
||||||
```js{5,11}
|
::: code-group
|
||||||
const nodes = [
|
|
||||||
|
```js [nodes <LogosJavascript />]
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const nodes = ref([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Node 1',
|
label: 'Node 1',
|
||||||
|
// this will create the node-type `custom`
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
position: { x: 50, y: 50 },
|
position: { x: 50, y: 50 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Node 1',
|
label: 'Node 1',
|
||||||
|
// this will create the node-type `special`
|
||||||
type: 'special',
|
type: 'special',
|
||||||
position: { x: 150, y: 50 },
|
position: { x: 150, y: 50 },
|
||||||
}
|
}
|
||||||
]
|
])
|
||||||
```
|
```
|
||||||
|
|
||||||
Vue Flow will now try to resolve this node-type to a component.
|
```vue [CustomNode.vue <LogosJavascript />]
|
||||||
First and foremost we will look for a definition in the `nodeTypes` object of the state.
|
<script setup>
|
||||||
After that we will try to resolve the component to a globally registered one that matches the exact name.
|
import { Position } from '@vue-flow/core'
|
||||||
Finally, we will check if a template slot has been provided to fill the node-type.
|
|
||||||
|
|
||||||
If none of these methods succeed in resolving the component the default node-type will be used as a fallback.
|
const props = defineProps(['label'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Handle type="target" :position="Position.Top" />
|
||||||
|
<div>{{ label }}</div>
|
||||||
|
<Handle type="source" :position="Position.Bottom" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts [nodes <LogosTypescript />]
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { Node } from '@vue-flow/core'
|
||||||
|
|
||||||
|
// You can pass 3 optional generic arguments to the Node interface, allowing you to define:
|
||||||
|
// 1. The data object type
|
||||||
|
// 2. The events object type
|
||||||
|
// 3. The possible node types
|
||||||
|
|
||||||
|
export interface CustomData {
|
||||||
|
hello: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomEvents {
|
||||||
|
onCustomEvent: (event: MouseEvent) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomNodeTypes = 'custom' | 'special'
|
||||||
|
|
||||||
|
type CustomNode = Node<CustomData, CustomEvents, CustomNodeTypes>
|
||||||
|
|
||||||
|
export const nodes = ref<CustomNode[]>([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Node 1',
|
||||||
|
// this will create the node-type `custom`
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 50, y: 50 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Node 1',
|
||||||
|
// this will create the node-type `special`
|
||||||
|
type: 'special',
|
||||||
|
position: { x: 150, y: 50 },
|
||||||
|
},
|
||||||
|
|
||||||
|
// this will throw a type error, as the type is not defined in the CustomEdgeTypes
|
||||||
|
// regardless it would be rendered as a default edge type
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Node 1',
|
||||||
|
type: 'invalid',
|
||||||
|
position: { x: 150, y: 50 },
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
```vue [CustomNode.vue <LogosTypescript />]
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { NodeProps } from '@vue-flow/core'
|
||||||
|
import { Position } from '@vue-flow/core'
|
||||||
|
|
||||||
|
import { CustomData, CustomEvents } from './nodes'
|
||||||
|
|
||||||
|
const props = defineProps<NodeProps<CustomData, CustomEvents>>()
|
||||||
|
|
||||||
|
console.log(props.data.hello) // 'world'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Handle type="target" :position="Position.Top" />
|
||||||
|
<div>{{ label }}</div>
|
||||||
|
<Handle type="source" :position="Position.Bottom" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
Vue Flow will then attempt to resolve this node-type to a component.
|
||||||
|
Priority is given to a definition in the nodeTypes object of the state.
|
||||||
|
Next, it tries to match the component to a globally registered one with the same name.
|
||||||
|
Finally, it searches for a provided template slot to fill in the node-type.
|
||||||
|
|
||||||
|
If no methods produce a result in resolving the component, the default node-type is used as a fallback.
|
||||||
|
|
||||||
### Template slots
|
### Template slots
|
||||||
|
|
||||||
The easiest way to define custom nodes is, by passing them as template slots.
|
One of the easiest ways to define custom nodes is, by passing them as template slots.
|
||||||
Your custom node-types are dynamically resolved to slot-names, meaning a node with the type `custom`
|
Dynamic resolution to slot-names is done for your user-defined node-types,
|
||||||
will expect a slot to have the name `node-custom`.
|
meaning a node with the type `custom` is expected to have a slot named `#node-custom`.
|
||||||
|
|
||||||
```vue{9,16}
|
```vue{9,18}
|
||||||
<script setup>
|
<script setup>
|
||||||
import { VueFlow } from '@vue-flow/core'
|
import { VueFlow } from '@vue-flow/core'
|
||||||
import CustomNode from './CustomNode.vue'
|
import CustomNode from './CustomNode.vue'
|
||||||
@@ -225,8 +443,10 @@ const elements = ref([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VueFlow v-model="elements">
|
<VueFlow v-model="elements">
|
||||||
|
<!-- the expected slot name is `node-custom` -->
|
||||||
<template #node-custom="props">
|
<template #node-custom="props">
|
||||||
<CustomNode v-bind="props" />
|
<CustomNode v-bind="props" />
|
||||||
</template>
|
</template>
|
||||||
@@ -236,13 +456,10 @@ const elements = ref([
|
|||||||
|
|
||||||
### Node-types object
|
### Node-types object
|
||||||
|
|
||||||
You can also define node-types by passing an object as a prop to the VueFlow component (or as an option to the
|
Alternatively, node-types can also be defined by passing an object as a prop to the VueFlow component (or as an option to the composable).
|
||||||
composable).
|
|
||||||
|
|
||||||
::: warning
|
::: warning
|
||||||
When doing this, mark your components as raw (using the designated function from the vue library) to avoid them being
|
Take precaution to mark your components as raw (utilizing the marked function from the Vue library) to prevent their conversion into reactive objects. Otherwise, Vue will display a warning on the console.
|
||||||
turned into reactive objects.
|
|
||||||
Otherwise, vue will throw a warning in the console.
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
```vue{6-9,26}
|
```vue{6-9,26}
|
||||||
@@ -269,60 +486,20 @@ const elements = ref([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 300px">
|
<VueFlow v-model="elements" :node-types="nodeTypes" />
|
||||||
<VueFlow v-model="elements" :node-types="nodeTypes" />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
You can find a more advanced example [here](/examples/nodes/).
|
[You can find a working example here](/examples/nodes/).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Node Template
|
## [Node Props](/typedocs/interfaces/NodeProps)
|
||||||
|
|
||||||
You can also set a template per node, which will overwrite the node-type component but will retain
|
Your custom nodes are enclosed so that fundamental functions like dragging or selecting operate.
|
||||||
the type otherwise.
|
But you may wish to expand on these features or implement your business logic inside nodes, thus your nodes receive the following properties:
|
||||||
|
|
||||||
```vue{17}
|
|
||||||
<script setup>
|
|
||||||
import { markRaw } from 'vue'
|
|
||||||
import CustomNode from './CustomNode.vue'
|
|
||||||
import OverwriteCustomNode from './OverwriteCustomNode.vue'
|
|
||||||
import SpecialNode from './SpecialNode.vue'
|
|
||||||
|
|
||||||
const nodeTypes = {
|
|
||||||
custom: markRaw(CustomNode),
|
|
||||||
special: markRaw(SpecialNode),
|
|
||||||
}
|
|
||||||
|
|
||||||
const elements = ref([
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
label: 'Node 1',
|
|
||||||
type: 'custom',
|
|
||||||
template: markRaw(OverwriteCustomNode),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
label: 'Node 1',
|
|
||||||
type: 'special',
|
|
||||||
}
|
|
||||||
])
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div style="height: 300px">
|
|
||||||
<VueFlow v-model="elements" :node-types="nodeTypes" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### [(Custom) Node Props](/typedocs/interfaces/NodeProps)
|
|
||||||
|
|
||||||
Your custom nodes are wrapped so that the basic functions like dragging or selecting work.
|
|
||||||
But you might want to extend on that functionality or implement your own business logic inside of nodes, therefore
|
|
||||||
your nodes receive the following props:
|
|
||||||
|
|
||||||
| Name | Definition | Type | Optional |
|
| Name | Definition | Type | Optional |
|
||||||
|------------------|--------------------------------------------------|------------------------------------------------------------|--------------------------------------------|
|
|------------------|--------------------------------------------------|------------------------------------------------------------|--------------------------------------------|
|
||||||
@@ -344,129 +521,240 @@ your nodes receive the following props:
|
|||||||
| sourcePosition | Source handle position | [Position](/typedocs/enums/Position) | <Check class="text-[var(--vp-c-brand)]" /> |
|
| sourcePosition | Source handle position | [Position](/typedocs/enums/Position) | <Check class="text-[var(--vp-c-brand)]" /> |
|
||||||
| dragHandle | Node drag handle class | string | <Check class="text-[var(--vp-c-brand)]" /> |
|
| dragHandle | Node drag handle class | string | <Check class="text-[var(--vp-c-brand)]" /> |
|
||||||
|
|
||||||
### (Custom) Node Events
|
## [Node Events](/typedocs/types/NodeEventsHandler)
|
||||||
|
|
||||||
In addition to the event handlers that you can access through [`useVueFlow`](/guide/composables#useVueFlow/) or the Vue
|
Vue Flow provides two main ways of listening to node events,
|
||||||
Flow component,
|
either by using `useVueFlow` to bind listeners to the event handlers
|
||||||
you can also pass in event handlers in your initial node definition, or you can access the node events through
|
or by using binding listeners to the `<VueFlow>` component.
|
||||||
the `events` prop passed
|
|
||||||
to your node components.
|
|
||||||
|
|
||||||
```vue{10-17}
|
::: code-group
|
||||||
|
|
||||||
|
```vue [useVueFlow]
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
|
// useVueFlow provides access to the event handlers
|
||||||
|
const {
|
||||||
|
onNodeDragStart,
|
||||||
|
onNodeDrag,
|
||||||
|
onNodeDragStop,
|
||||||
|
onNodeClick,
|
||||||
|
onNodeDoubleClick,
|
||||||
|
onNodeContextMenu,
|
||||||
|
onNodeMouseEnter,
|
||||||
|
onNodeMouseLeave,
|
||||||
|
onNodeMouseMove
|
||||||
|
} = useVueFlow()
|
||||||
|
|
||||||
|
const elements = ref([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Node 1',
|
||||||
|
position: { x: 50, y: 50 },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// bind listeners to the event handlers
|
||||||
|
onNodeDragStart((event) => {
|
||||||
|
console.log('Node drag started', event)
|
||||||
|
})
|
||||||
|
|
||||||
|
onNodeDrag((event) => {
|
||||||
|
console.log('Node dragged', event)
|
||||||
|
})
|
||||||
|
|
||||||
|
onNodeDragStop((event) => {
|
||||||
|
console.log('Node drag stopped', event)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ... and so on
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VueFlow v-model="elements" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
```vue [component]
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
import { VueFlow } from '@vue-flow/core'
|
import { VueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
const elements = ref([
|
const elements = ref([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Node 1',
|
label: 'Node 1',
|
||||||
type: 'custom',
|
|
||||||
position: { x: 50, y: 50 },
|
position: { x: 50, y: 50 },
|
||||||
events: {
|
},
|
||||||
click: () => {
|
|
||||||
console.log('Node 1 clicked')
|
|
||||||
},
|
|
||||||
customEvent: () => {
|
|
||||||
console.log('Node 1 custom event')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
```
|
|
||||||
|
|
||||||
As you can see above, you can also pass in custom event handlers. These will not be called by Vue Flow but can be used
|
|
||||||
to forward callback functions to your custom components.
|
|
||||||
The `click` handler is part of the [`NodeEventsHandler`](/typedocs/types/NodeEventsHandler) interface, meaning it
|
|
||||||
will be
|
|
||||||
triggered when the node is clicked.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { NodeProps, NodeEventsOn } from '@vue-flow/core'
|
|
||||||
|
|
||||||
// define your events
|
|
||||||
interface CustomNodeEvents {
|
|
||||||
click: NodeEventsOn['click']
|
|
||||||
customEvent: (input: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CustomNodeProps extends NodeProps<any, CustomNodeEvents> {
|
|
||||||
id: string
|
|
||||||
events: CustomNodeEvents
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<CustomNodeProps>()
|
|
||||||
|
|
||||||
props.events.click(() => {
|
|
||||||
console.log(`Node ${props.id} clicked`)
|
|
||||||
})
|
|
||||||
|
|
||||||
// custom events are just functions, they are not hooks which you can listen to like `click`
|
|
||||||
props.events.customEvent('custom event triggered')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Omitted for simplicty -->
|
<!-- bind listeners to the event handlers -->
|
||||||
|
<VueFlow
|
||||||
|
v-model="elements"
|
||||||
|
@node-drag-start="console.log('drag start', $event)"
|
||||||
|
@node-drag="console.log('drag', $event)"
|
||||||
|
@node-drag-stop="console.log('drag stop', $event)"
|
||||||
|
@node-click="console.log('click', $event)"
|
||||||
|
@node-double-click="console.log('dblclick', $event)"
|
||||||
|
@node-contextmenu="console.log('contextmenu', $event)"
|
||||||
|
@node-mouse-enter="console.log('mouseenter', $event)"
|
||||||
|
@node-mouse-leave="console.log('mouseleave', $event)"
|
||||||
|
@node-mouse-move="console.log('mousemove', $event)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Styling
|
|
||||||
|
|
||||||
::: tip
|
|
||||||
To overwrite default theme styles check the [Theming section](/guide/theming).
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Custom Nodes
|
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
|
||||||
|
<VueFlow
|
||||||
|
v-model="defaultNode"
|
||||||
|
@node-drag-start="console.log('drag start', $event)"
|
||||||
|
@node-drag="console.log('drag', $event)"
|
||||||
|
@node-drag-stop="console.log('drag stop', $event)"
|
||||||
|
@node-click="console.log('click', $event)"
|
||||||
|
@node-double-click="console.log('dblclick', $event)"
|
||||||
|
@node-contextmenu="console.log('contextmenu', $event)"
|
||||||
|
@node-mouse-enter="console.log('mouseenter', $event)"
|
||||||
|
@node-mouse-leave="console.log('mouseleave', $event)"
|
||||||
|
@node-mouse-move="console.log('mousemove', $event)"
|
||||||
|
>
|
||||||
|
<Panel position="top-center">
|
||||||
|
<p class="text-sm">Interact to see events in browser console</p>
|
||||||
|
</Panel>
|
||||||
|
<Background class="rounded-lg" />
|
||||||
|
</VueFlow>
|
||||||
|
</div>
|
||||||
|
|
||||||
When you create a new node type you also need to implement some styling. Your custom node has no default styles.
|
|
||||||
|
## Customizing Appearance
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
To override the styles of the default theme, visit the [Theming section](/guide/theming).
|
||||||
|
:::
|
||||||
|
|
||||||
|
### User-Defined Nodes
|
||||||
|
|
||||||
|
When constructing a new node type, it's necessary for you to add some styling specific to it.
|
||||||
|
User-created nodes don't have any default styles associated and thus need custom styling.
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.vue-flow__node-custom {
|
.vue-flow__node-custom {
|
||||||
background: #9CA8B3;
|
background: #9CA8B3;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scrolling inside a node
|
## Implementing Scrolling within Nodes
|
||||||
|
|
||||||
|
Sometimes, a node might contain a large amount of content, making it difficult for users to view everything without the aid of a scroll function.
|
||||||
|
To facilitate this scrolling ability without invoking zoom or pan behaviors on the node, Vue Flow provides the `noWheelClassName` property.
|
||||||
|
|
||||||
|
The `noWheelClassName` property allows you to specify a class name that, when applied to a node, will disable the default zoom-on-scroll or pan-on-scroll events on that particular node.
|
||||||
|
|
||||||
You can use the `noWheelClassName` prop to define a class name which will prevent zoom-on-scroll or pan-on-scroll behavior on
|
|
||||||
that node.
|
|
||||||
By default, the `noWheelClassName` is `nowheel`.
|
By default, the `noWheelClassName` is `nowheel`.
|
||||||
By adding this class you can enable scrolling inside a node without triggering zoom-pan behavior.
|
|
||||||
|
|
||||||
## Dragging inside a node
|
|
||||||
|
|
||||||
You can use the `noDragClassName` prop to define a class name which will not trigger dragging behavior on
|
|
||||||
that node.
|
|
||||||
By default, the `noDragClassName` is `nodrag`.
|
|
||||||
|
|
||||||
## Dynamic handle positions / Adding handles dynamically
|
|
||||||
|
|
||||||
|
|
||||||
::: info
|
|
||||||
When using Vue Flow 1.x you don't need to call `updateNodeInternals` when adding handles dynamically.
|
|
||||||
Handles will try to be added to the node automatically when they are mounted.
|
|
||||||
If this does not work for you, for whatever reason, you can still follow the guide below and force Vue Flow to update
|
|
||||||
the node internals.
|
|
||||||
:::
|
|
||||||
|
|
||||||
When working with dynamic handle positions or adding handles dynamically, you need to use
|
|
||||||
the [`updateNodeInternals`](/typedocs/types/UpdateNodeInternals) method.
|
|
||||||
|
|
||||||
You need to call this method otherwise your node will not respond to the new handles and edges will be
|
|
||||||
misaligned.
|
|
||||||
|
|
||||||
You can either use the store action to update multiple nodes at once by passing their ids into the method,
|
|
||||||
or you can emit the `updateNodeInternals` event from your custom node component without passing any parameters.
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
- Using store action
|
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const listItems = ref(Array.from({ length: 100 }, (_, i) => i))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="custom-node-container">
|
||||||
|
<ul class="nowheel">
|
||||||
|
<li v-for="item in listItems" :key="item">Item {{ item }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
|
||||||
|
<VueFlow :model-value="[{ id: '1', type: 'scrollable', label: 'Node 1', position: { x: 50, y: 50 } }]">
|
||||||
|
<template #node-scrollable>
|
||||||
|
<ScrollableNode />
|
||||||
|
</template>
|
||||||
|
<Background class="rounded-lg" />
|
||||||
|
</VueFlow>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.vue-flow__node-scrollable {
|
||||||
|
@apply bg-accent rounded px-2;
|
||||||
|
overflow: auto;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-node-container ul {
|
||||||
|
max-height: 75px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
## Preventing Drag Behavior withing Nodes
|
||||||
|
|
||||||
|
There are certain scenarios where you might need to interact with the contents of a node without triggering a drag action on the node itself.
|
||||||
|
This can be particularly useful when nodes contain interactive elements like input boxes, buttons, or sliders that you want your users to engage with.
|
||||||
|
|
||||||
|
To accomplish this, Vue Flow provides a `noDragClassName` property.
|
||||||
|
This property allows specification of a class name, which when applied to an element within a node,
|
||||||
|
prevents triggering a drag action on the node when the user interacts with that element.
|
||||||
|
|
||||||
|
By default, the `noDragClassName` is set as `nodrag`.
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const inputValue = ref('')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="custom-node-container">
|
||||||
|
<input class="nodrag" v-model="inputValue" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
<div class="mt-4 bg-[var(--vp-code-block-bg)] rounded-lg h-50">
|
||||||
|
<VueFlow :model-value="[{ id: '1', type: 'input-field', label: 'Node 1', position: { x: 50, y: 50 } }]">
|
||||||
|
<template #node-input-field>
|
||||||
|
<InputFieldNode />
|
||||||
|
</template>
|
||||||
|
<Background class="rounded-lg" />
|
||||||
|
</VueFlow>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.vue-flow__node-input-field {
|
||||||
|
@apply bg-primary rounded p-4;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
## Working with Dynamic Handle Positions / Adding Handles Dynamically
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
In Vue Flow 1.x, there's no need to manually invoke `updateNodeInternals` when dynamically adding handles.
|
||||||
|
Upon mounting, handles will automatically attempt to attach to the node.
|
||||||
|
However, if for any reason this isn't happening as expected, you can stick to the guideline provided below to enforce Vue Flow to update the node internals.
|
||||||
|
:::
|
||||||
|
|
||||||
|
At times, you may need to modify handle positions dynamically or programmatically add new handles to a node. In this scenario, the [`updateNodeInternals`](/typedocs/types/UpdateNodeInternals) method found in Vue Flow's API comes in handy.
|
||||||
|
|
||||||
|
Invoking this method is vital when dealing with dynamic handles. If not, the node might fail to recognize these new handles, resulting in misaligned edges.
|
||||||
|
|
||||||
|
The `updateNodeInternals` function can be deployed in one of two ways:
|
||||||
|
|
||||||
|
- **Using the store action:** This approach allows you to update several nodes at once by passing their IDs into the method.
|
||||||
|
- **Emitting the `updateNodeInternals` event from your customized node component:** This doesn't require any parameters to be passed.
|
||||||
|
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```js [store action]
|
||||||
import { useVueFlow } from '@vue-flow/core'
|
import { useVueFlow } from '@vue-flow/core'
|
||||||
|
|
||||||
const { updateNodeInternals } = useVueFlow()
|
const { updateNodeInternals } = useVueFlow()
|
||||||
@@ -474,12 +762,9 @@ const { updateNodeInternals } = useVueFlow()
|
|||||||
const onSomeEvent = () => {
|
const onSomeEvent = () => {
|
||||||
updateNodeInternals(['1'])
|
updateNodeInternals(['1'])
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Emitting event from custom component
|
```vue [emit event]
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const emits = defineEmits(['updateNodeInternals'])
|
const emits = defineEmits(['updateNodeInternals'])
|
||||||
|
|
||||||
@@ -488,3 +773,5 @@ const onSomeEvent = () => {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|||||||
Reference in New Issue
Block a user