Skip to content

Commit 3220e20

Browse files
committed
docs: add dagre layout example
1 parent 0bfb5f4 commit 3220e20

File tree

13 files changed

+192
-40
lines changed

13 files changed

+192
-40
lines changed

docs/components/DocsRepl.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import CodeMirror from '@vue/repl/codemirror-editor'
55
import { useVueFlow } from '@vue-flow/core'
66
import { exampleImports } from '../examples'
77
8-
const props = defineProps<{ example: keyof typeof exampleImports; mainFile?: string; dependencies?: Record<string, string> }>()
8+
const props = defineProps<{ example: keyof typeof exampleImports; mainFile?: string }>()
99
1010
const { vueFlowVersion } = useVueFlow()
1111

docs/components/Repl.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Suspense } from 'vue'
44
const DocsRepl = defineAsyncComponent(() => import('./DocsRepl.vue'))
55
66
export default defineComponent({
7-
props: ['example', 'examplesImports', 'dependencies'],
7+
props: ['example', 'mainFile'],
88
setup(props) {
99
return () => {
1010
if (typeof navigator === 'undefined') {

docs/examples/custom-node/App.vue

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
<script setup>
2-
import { ref } from 'vue'
2+
import { ref, toRef } from 'vue'
33
import { MiniMap } from '@vue-flow/minimap'
4-
import { Position, VueFlow, useNodesData, useVueFlow } from '@vue-flow/core'
4+
import { Position, VueFlow } from '@vue-flow/core'
55
import ColorSelectorNode from './ColorSelectorNode.vue'
66
import OutputNode from './OutputNode.vue'
77
import { presets } from './presets.js'
88
9-
useVueFlow()
10-
11-
const nodeData = useNodesData('1')
12-
139
const nodes = ref([
1410
{
1511
id: '1',
@@ -25,6 +21,8 @@ const nodes = ref([
2521
},
2622
])
2723
24+
const colorSelectorData = toRef(() => nodes.value[0].data)
25+
2826
const edges = ref([
2927
{
3028
id: 'e1a-2',
@@ -33,7 +31,7 @@ const edges = ref([
3331
target: '2',
3432
animated: true,
3533
style: () => ({
36-
stroke: nodeData.value?.color,
34+
stroke: colorSelectorData.value?.color,
3735
filter: 'invert(100%)',
3836
}),
3937
},
@@ -55,7 +53,7 @@ function nodeStroke(n) {
5553
5654
function nodeColor(n) {
5755
if (n.type === 'color-selector') {
58-
return nodeData.value?.color
56+
return colorSelectorData.value?.color
5957
}
6058
6159
return '#fff'
@@ -64,20 +62,20 @@ function nodeColor(n) {
6462
6563
<template>
6664
<VueFlow
67-
:nodes="nodes"
65+
v-model:nodes="nodes"
6866
:edges="edges"
6967
class="custom-node-flow"
70-
:class="[nodeData?.isGradient ? 'animated-bg-gradient' : '']"
71-
:style="{ backgroundColor: nodeData?.color }"
68+
:class="[colorSelectorData?.isGradient ? 'animated-bg-gradient' : '']"
69+
:style="{ backgroundColor: colorSelectorData?.color }"
7270
:default-viewport="{ zoom: 1.5 }"
7371
fit-view-on-init
7472
>
75-
<template #node-color-selector="{ data }">
76-
<ColorSelectorNode :data="data" />
73+
<template #node-color-selector="{ id }">
74+
<ColorSelectorNode :id="id" />
7775
</template>
7876
79-
<template #node-output="{ data }">
80-
<OutputNode :data="data" />
77+
<template #node-output>
78+
<OutputNode />
8179
</template>
8280
8381
<MiniMap :node-stroke-color="nodeStroke" :node-color="nodeColor" />

docs/examples/custom-node/ColorSelectorNode.vue

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
<script setup>
2-
import { Handle, Position, useNodeId, useVueFlow } from '@vue-flow/core'
2+
import { Handle, Position, useVueFlow } from '@vue-flow/core'
33
import { colors } from './presets.js'
44
5-
defineProps({
6-
data: {
7-
type: Object,
5+
const props = defineProps({
6+
id: {
7+
type: String,
88
required: true,
99
},
1010
})
1111
12-
const nodeId = useNodeId()
13-
1412
const { updateNodeData } = useVueFlow()
1513
1614
function onSelect(color) {
17-
updateNodeData(nodeId, { color }, { replace: true })
15+
updateNodeData(props.id, { color, isGradient: false })
1816
}
1917
2018
function onGradient() {
21-
updateNodeData(nodeId, { isGradient: true }, { replace: true })
19+
updateNodeData(props.id, { isGradient: true })
2220
}
2321
</script>
2422

docs/examples/custom-node/OutputNode.vue

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
<script setup>
22
import { Handle, Position, useHandleConnections, useNodesData } from '@vue-flow/core'
33
4-
defineProps({
5-
data: {
6-
type: Object,
7-
required: true,
8-
},
9-
})
10-
114
const connections = useHandleConnections({
125
type: 'target',
136
})

docs/examples/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { IntersectionApp, IntersectionCSS } from './intersection'
1818
import { SnapToHandleApp, SnappableConnectionLine } from './connection-radius'
1919
import { NodeResizerApp, ResizableNode } from './node-resizer'
2020
import { ToolbarApp, ToolbarNode } from './node-toolbar'
21+
import { LayoutApp, LayoutElements } from './layout'
2122

2223
export const exampleImports = {
2324
basic: {
@@ -123,4 +124,11 @@ export const exampleImports = {
123124
'App.vue': ToolbarApp,
124125
'ToolbarNode.vue': ToolbarNode,
125126
},
127+
layout: {
128+
'App.vue': LayoutApp,
129+
'initial-elements.js': LayoutElements,
130+
'additionalImports': {
131+
dagre: 'https://cdn.skypack.dev/pin/[email protected]/mode=imports,min/optimized/dagre.js',
132+
},
133+
},
126134
}

docs/examples/layout/App.vue

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<script setup>
2+
import dagre from 'dagre'
3+
import { nextTick, ref } from 'vue'
4+
import { Panel, Position, VueFlow, useVueFlow } from '@vue-flow/core'
5+
6+
import { initialEdges, initialNodes } from './initial-elements.js'
7+
8+
const nodes = ref(initialNodes)
9+
10+
const edges = ref(initialEdges)
11+
12+
const { findNode, fitView } = useVueFlow()
13+
14+
function handleLayout(direction) {
15+
// we create a new graph instance, in case some nodes/edges were removed, otherwise dagre would act as if they were still there
16+
const dagreGraph = new dagre.graphlib.Graph()
17+
18+
dagreGraph.setDefaultEdgeLabel(() => ({}))
19+
20+
const isHorizontal = direction === 'LR'
21+
dagreGraph.setGraph({ rankdir: direction })
22+
23+
for (const node of nodes.value) {
24+
// if you need width+height of nodes for your layout, you can use the dimensions property of the internal node (`GraphNode` type)
25+
const graphNode = findNode(node.id)
26+
27+
dagreGraph.setNode(node.id, { width: graphNode.dimensions.width || 150, height: graphNode.dimensions.height || 50 })
28+
}
29+
30+
for (const edge of edges.value) {
31+
dagreGraph.setEdge(edge.source, edge.target)
32+
}
33+
34+
dagre.layout(dagreGraph)
35+
36+
// set nodes with updated positions
37+
nodes.value = nodes.value.map((node) => {
38+
const nodeWithPosition = dagreGraph.node(node.id)
39+
40+
return {
41+
...node,
42+
targetPosition: isHorizontal ? Position.Left : Position.Top,
43+
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
44+
position: { x: nodeWithPosition.x, y: nodeWithPosition.y },
45+
}
46+
})
47+
48+
nextTick(() => {
49+
fitView()
50+
})
51+
}
52+
</script>
53+
54+
<template>
55+
<div class="layoutflow">
56+
<VueFlow :nodes="nodes" :edges="edges" @nodes-initialized="handleLayout('TB')">
57+
<Panel style="display: flex; gap: 1rem" position="top-right">
58+
<button @click="handleLayout('TB')">vertical layout</button>
59+
<button @click="handleLayout('LR')">horizontal layout</button>
60+
</Panel>
61+
</VueFlow>
62+
</div>
63+
</template>
64+
65+
<style>
66+
.layoutflow {
67+
height: 100%;
68+
width: 100%;
69+
}
70+
</style>

docs/examples/layout/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as LayoutApp } from './App.vue?raw'
2+
export { default as LayoutElements } from './initial-elements.js?raw'
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const position = { x: 0, y: 0 }
2+
3+
export const initialNodes = [
4+
{
5+
id: '1',
6+
type: 'input',
7+
label: 'input',
8+
position,
9+
},
10+
{
11+
id: '2',
12+
label: 'node 2',
13+
position,
14+
},
15+
{
16+
id: '2a',
17+
label: 'node 2a',
18+
position,
19+
},
20+
{
21+
id: '2b',
22+
label: 'node 2b',
23+
position,
24+
},
25+
{
26+
id: '2c',
27+
label: 'node 2c',
28+
position,
29+
},
30+
{
31+
id: '2d',
32+
label: 'node 2d',
33+
position,
34+
},
35+
{
36+
id: '3',
37+
label: 'node 3',
38+
position,
39+
},
40+
{
41+
id: '4',
42+
label: 'node 4',
43+
position,
44+
},
45+
{
46+
id: '5',
47+
label: 'node 5',
48+
position,
49+
},
50+
{
51+
id: '6',
52+
type: 'output',
53+
label: 'output',
54+
position,
55+
},
56+
]
57+
58+
export const initialEdges = [
59+
{ id: '7', type: 'output', label: 'output', position: { x: 400, y: 450 } },
60+
{ id: 'e12', source: '1', target: '2', type: 'smoothstep', animated: true },
61+
{ id: 'e13', source: '1', target: '3', type: 'smoothstep', animated: true },
62+
{ id: 'e22a', source: '2', target: '2a', type: 'smoothstep', animated: true },
63+
{ id: 'e22b', source: '2', target: '2b', type: 'smoothstep', animated: true },
64+
{ id: 'e22c', source: '2', target: '2c', type: 'smoothstep', animated: true },
65+
{ id: 'e2c2d', source: '2c', target: '2d', type: 'smoothstep', animated: true },
66+
67+
{ id: 'e45', source: '4', target: '5', type: 'smoothstep', animated: true },
68+
{ id: 'e56', source: '5', target: '6', type: 'smoothstep', animated: true },
69+
{ id: 'e57', source: '5', target: '7', type: 'smoothstep', animated: true },
70+
]

docs/examples/stress/App.vue

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,19 @@ const edges = ref(initialEdges)
1313
1414
const { dimensions, fitView } = useVueFlow()
1515
16-
function toggleClass() {
17-
nodes.value = nodes.value.map((node) => ({ ...node, class: node.class === 'light' ? 'dark' : 'light' }))
18-
}
19-
2016
function updatePos() {
2117
nodes.value = nodes.value.map((node) => {
2218
return {
2319
...node,
2420
position: {
25-
x: Math.random() * 10 * dimensions.value.width,
26-
y: Math.random() * 10 * dimensions.value.height,
21+
x: Math.random() * dimensions.value.width,
22+
y: Math.random() * dimensions.value.height,
2723
},
2824
}
2925
})
3026
3127
nextTick(() => {
32-
fitView({ duration: 1000, padding: 0.5 })
28+
fitView({ padding: 0.5 })
3329
})
3430
}
3531
</script>
@@ -42,7 +38,6 @@ function updatePos() {
4238

4339
<Panel position="top-right">
4440
<button style="margin-right: 5px" @click="updatePos">update positions</button>
45-
<button @click="toggleClass">toggle class</button>
4641
</Panel>
4742
</VueFlow>
4843
</template>

0 commit comments

Comments
 (0)