Skip to content

Commit

Permalink
docs(examples): add simple layout example (#1678)
Browse files Browse the repository at this point in the history
* docs(examples): cleanup animated layout example

Signed-off-by: braks <[email protected]>

* chore(docs,deps): update deps

Signed-off-by: braks <[email protected]>

* docs(examples): add animated and simple layout examples

Signed-off-by: braks <[email protected]>

---------

Signed-off-by: braks <[email protected]>
  • Loading branch information
bcakmakoglu authored Nov 5, 2024
1 parent f3075bc commit 61f4b0d
Show file tree
Hide file tree
Showing 13 changed files with 947 additions and 351 deletions.
11 changes: 11 additions & 0 deletions docs/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { SnapToHandleApp, SnappableConnectionLine } from './connection-radius'
import { NodeResizerApp, ResizableNode } from './node-resizer'
import { ToolbarApp, ToolbarNode } from './node-toolbar'
import { LayoutApp, LayoutEdge, LayoutElements, LayoutIcon, LayoutNode, useLayout, useRunProcess, useShuffle } from './layout'
import { SimpleLayoutApp, SimpleLayoutElements, SimpleLayoutIcon, useSimpleLayout } from './layout-simple'

import { MathApp, MathCSS, MathElements, MathIcon, MathOperatorNode, MathResultNode, MathValueNode } from './math'
import { ConfirmApp, ConfirmDialog, useDialog } from './confirm-delete'

Expand Down Expand Up @@ -140,6 +142,15 @@ export const exampleImports = {
'@dagrejs/dagre': 'https://cdn.jsdelivr.net/npm/@dagrejs/[email protected]/+esm',
},
},
layoutSimple: {
'App.vue': SimpleLayoutApp,
'initial-elements.js': SimpleLayoutElements,
'useLayout.js': useSimpleLayout,
'Icon.vue': SimpleLayoutIcon,
'additionalImports': {
'@dagrejs/dagre': 'https://cdn.jsdelivr.net/npm/@dagrejs/[email protected]/+esm',
},
},
math: {
'App.vue': MathApp,
'ValueNode.vue': MathValueNode,
Expand Down
133 changes: 133 additions & 0 deletions docs/examples/layout-simple/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<script setup>
import { nextTick, ref } from 'vue'
import { Panel, VueFlow, useVueFlow } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import Icon from './Icon.vue'
import { initialEdges, initialNodes } from './initial-elements.js'
import { useLayout } from './useLayout'
const nodes = ref(initialNodes)
const edges = ref(initialEdges)
const { layout } = useLayout()
const { fitView } = useVueFlow()
async function layoutGraph(direction) {
nodes.value = layout(nodes.value, edges.value, direction)
nextTick(() => {
fitView()
})
}
</script>

<template>
<div class="layout-flow">
<VueFlow :nodes="nodes" :edges="edges" @nodes-initialized="layoutGraph('LR')">
<Background />

<Panel class="process-panel" position="top-right">
<div class="layout-panel">
<button title="set horizontal layout" @click="layoutGraph('LR')">
<Icon name="horizontal" />
</button>

<button title="set vertical layout" @click="layoutGraph('TB')">
<Icon name="vertical" />
</button>
</div>
</Panel>
</VueFlow>
</div>
</template>

<style>
.layout-flow {
background-color: #1a192b;
height: 100%;
width: 100%;
}
.process-panel,
.layout-panel {
display: flex;
gap: 10px;
}
.process-panel {
background-color: #2d3748;
padding: 10px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
}
.process-panel button {
border: none;
cursor: pointer;
background-color: #4a5568;
border-radius: 8px;
color: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.process-panel button {
font-size: 16px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.checkbox-panel {
display: flex;
align-items: center;
gap: 10px;
}
.process-panel button:hover,
.layout-panel button:hover {
background-color: #2563eb;
transition: background-color 0.2s;
}
.process-panel label {
color: white;
font-size: 12px;
}
.stop-btn svg {
display: none;
}
.stop-btn:hover svg {
display: block;
}
.stop-btn:hover .spinner {
display: none;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #2563eb;
border-radius: 50%;
width: 10px;
height: 10px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
40 changes: 40 additions & 0 deletions docs/examples/layout-simple/Icon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script setup>
defineProps({
name: {
type: String,
required: true,
},
})
</script>

<template>
<svg v-if="name === 'play'" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M8 5v14l11-7z" fill="currentColor" />
</svg>

<svg v-else-if="name === 'stop'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M8 16h8V8H8zm4 6q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22"
/>
</svg>

<svg v-else-if="name === 'horizontal'" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M2,12 L22,12" stroke="currentColor" stroke-width="2" />
<path d="M7,7 L2,12 L7,17" stroke="currentColor" stroke-width="2" fill="none" />
<path d="M17,7 L22,12 L17,17" stroke="currentColor" stroke-width="2" fill="none" />
</svg>

<svg v-else-if="name === 'vertical'" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12,2 L12,22" stroke="currentColor" stroke-width="2" />
<path d="M7,7 L12,2 L17,7" stroke="currentColor" stroke-width="2" fill="none" />
<path d="M7,17 L12,22 L17,17" stroke="currentColor" stroke-width="2" fill="none" />
</svg>

<svg v-else-if="name === 'shuffle'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M14 20v-2h2.6l-3.175-3.175L14.85 13.4L18 16.55V14h2v6zm-8.6 0L4 18.6L16.6 6H14V4h6v6h-2V7.4zm3.775-9.425L4 5.4L5.4 4l5.175 5.175z"
/>
</svg>
</template>
4 changes: 4 additions & 0 deletions docs/examples/layout-simple/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as SimpleLayoutApp } from './App.vue?raw'
export { default as SimpleLayoutElements } from './initial-elements.js?raw'
export { default as useSimpleLayout } from './useLayout.js?raw'
export { default as SimpleLayoutIcon } from './Icon.vue?raw'
94 changes: 94 additions & 0 deletions docs/examples/layout-simple/initial-elements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const position = { x: 0, y: 0 }

export const initialNodes = [
{
id: '1',
position,
data: {
label: 'Node 1',
},
},
{
id: '2',
position,
data: {
label: 'Node 2',
},
},
{
id: '2a',
position,
data: {
label: 'Node 2a',
},
},
{
id: '2b',
position,
data: {
label: 'Node 2b',
},
},
{
id: '2c',
position,
data: {
label: 'Node 2c',
},
},
{
id: '2d',
position,
data: {
label: 'Node 2d',
},
},
{
id: '3',
position,
data: {
label: 'Node 3',
},
},
{
id: '4',
position,
data: {
label: 'Node 4',
},
},
{
id: '5',
position,
data: {
label: 'Node 5',
},
},
{
id: '6',
position,
data: {
label: 'Node 6',
},
},
{
id: '7',
position,
data: {
label: 'Node 7',
},
},
]

export const initialEdges = [
{ id: 'e1-2', source: '1', target: '2' },
{ id: 'e1-3', source: '1', target: '3' },
{ id: 'e2-2a', source: '2', target: '2a' },
{ id: 'e2-2b', source: '2', target: '2b' },
{ id: 'e2-2c', source: '2', target: '2c' },
{ id: 'e2c-2d', source: '2c', target: '2d' },
{ id: 'e3-7', source: '3', target: '4' },
{ id: 'e4-5', source: '4', target: '5' },
{ id: 'e5-6', source: '5', target: '6' },
{ id: 'e5-7', source: '5', target: '7' },
]
56 changes: 56 additions & 0 deletions docs/examples/layout-simple/useLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import dagre from '@dagrejs/dagre'
import { Position, useVueFlow } from '@vue-flow/core'
import { ref } from 'vue'

/**
* Composable to run the layout algorithm on the graph.
* It uses the `dagre` library to calculate the layout of the nodes and edges.
*/
export function useLayout() {
const { findNode } = useVueFlow()

const graph = ref(new dagre.graphlib.Graph())

const previousDirection = ref('LR')

function layout(nodes, edges, direction) {
// we create a new graph instance, in case some nodes/edges were removed, otherwise dagre would act as if they were still there
const dagreGraph = new dagre.graphlib.Graph()

graph.value = dagreGraph

dagreGraph.setDefaultEdgeLabel(() => ({}))

const isHorizontal = direction === 'LR'
dagreGraph.setGraph({ rankdir: direction })

previousDirection.value = direction

for (const node of nodes) {
// if you need width+height of nodes for your layout, you can use the dimensions property of the internal node (`GraphNode` type)
const graphNode = findNode(node.id)

dagreGraph.setNode(node.id, { width: graphNode.dimensions.width || 150, height: graphNode.dimensions.height || 50 })
}

for (const edge of edges) {
dagreGraph.setEdge(edge.source, edge.target)
}

dagre.layout(dagreGraph)

// set nodes with updated positions
return nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id)

return {
...node,
targetPosition: isHorizontal ? Position.Left : Position.Top,
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
position: { x: nodeWithPosition.x, y: nodeWithPosition.y },
}
})
}

return { graph, layout, previousDirection }
}
7 changes: 6 additions & 1 deletion docs/examples/layout/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ async function layoutGraph(direction) {

<template>
<div class="layout-flow">
<VueFlow :nodes="nodes" :edges="edges" @nodes-initialized="layoutGraph('LR')">
<VueFlow
:nodes="nodes"
:edges="edges"
:default-edge-options="{ type: 'animation', animated: true }"
@nodes-initialized="layoutGraph('LR')"
>
<template #node-process="props">
<ProcessNode :data="props.data" :source-position="props.sourcePosition" :target-position="props.targetPosition" />
</template>
Expand Down
Loading

0 comments on commit 61f4b0d

Please sign in to comment.