diff --git a/docs/examples/index.ts b/docs/examples/index.ts index 0ba4d3b7..cf9f6141 100644 --- a/docs/examples/index.ts +++ b/docs/examples/index.ts @@ -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' @@ -140,6 +142,15 @@ export const exampleImports = { '@dagrejs/dagre': 'https://cdn.jsdelivr.net/npm/@dagrejs/dagre@1.1.2/+esm', }, }, + layoutSimple: { + 'App.vue': SimpleLayoutApp, + 'initial-elements.js': SimpleLayoutElements, + 'useLayout.js': useSimpleLayout, + 'Icon.vue': SimpleLayoutIcon, + 'additionalImports': { + '@dagrejs/dagre': 'https://cdn.jsdelivr.net/npm/@dagrejs/dagre@1.1.2/+esm', + }, + }, math: { 'App.vue': MathApp, 'ValueNode.vue': MathValueNode, diff --git a/docs/examples/layout-simple/App.vue b/docs/examples/layout-simple/App.vue new file mode 100644 index 00000000..2e2ed4f4 --- /dev/null +++ b/docs/examples/layout-simple/App.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/docs/examples/layout-simple/Icon.vue b/docs/examples/layout-simple/Icon.vue new file mode 100644 index 00000000..a69d9cc7 --- /dev/null +++ b/docs/examples/layout-simple/Icon.vue @@ -0,0 +1,40 @@ + + + diff --git a/docs/examples/layout-simple/index.ts b/docs/examples/layout-simple/index.ts new file mode 100644 index 00000000..30166481 --- /dev/null +++ b/docs/examples/layout-simple/index.ts @@ -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' diff --git a/docs/examples/layout-simple/initial-elements.js b/docs/examples/layout-simple/initial-elements.js new file mode 100644 index 00000000..2c2f3b38 --- /dev/null +++ b/docs/examples/layout-simple/initial-elements.js @@ -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' }, +] diff --git a/docs/examples/layout-simple/useLayout.js b/docs/examples/layout-simple/useLayout.js new file mode 100644 index 00000000..652aced6 --- /dev/null +++ b/docs/examples/layout-simple/useLayout.js @@ -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 } +} diff --git a/docs/examples/layout/App.vue b/docs/examples/layout/App.vue index c6123bbb..766f77f2 100644 --- a/docs/examples/layout/App.vue +++ b/docs/examples/layout/App.vue @@ -52,7 +52,12 @@ async function layoutGraph(direction) {