From f9e4d84297065dd666108a3ec83eb217c22afae5 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Tue, 6 Feb 2024 23:36:49 +0100 Subject: [PATCH] chore(docs): update layout example styles --- docs/examples/layout/App.vue | 47 ++++++-- docs/examples/layout/ProcessNode.vue | 35 +++--- docs/examples/layout/initial-elements.js | 1 + docs/examples/layout/useRunProcess.js | 100 ++++++++++++------ .../vite/src/Layouting/LayoutingExample.vue | 55 ++++++++-- examples/vite/src/Layouting/ProcessNode.vue | 34 +++--- .../vite/src/Layouting/initial-elements.ts | 2 +- examples/vite/src/Layouting/useRunProcess.ts | 100 ++++++++++++------ 8 files changed, 252 insertions(+), 122 deletions(-) diff --git a/docs/examples/layout/App.vue b/docs/examples/layout/App.vue index 53a50541..1ccc05ac 100644 --- a/docs/examples/layout/App.vue +++ b/docs/examples/layout/App.vue @@ -14,9 +14,7 @@ const edges = ref(initialEdges) const dagreGraph = ref(new dagre.graphlib.Graph()) -dagreGraph.value.setDefaultEdgeLabel(() => ({})) - -const { run } = useRunProcess() +const { run, stop, isRunning } = useRunProcess(dagreGraph) const { findNode, fitView } = useVueFlow() @@ -69,11 +67,14 @@ function handleLayout(direction) { - + + - + + + @@ -81,12 +82,42 @@ function handleLayout(direction) { diff --git a/docs/examples/layout/ProcessNode.vue b/docs/examples/layout/ProcessNode.vue index f97c3c3b..d0f297cd 100644 --- a/docs/examples/layout/ProcessNode.vue +++ b/docs/examples/layout/ProcessNode.vue @@ -24,12 +24,11 @@ const bgColor = toRef(() => { return '#10b981' } - if (props.data.isRunning || props.data.isSkipped) { - // pick me a lighter color please - return '#6b7280' + if (props.data.isCancelled) { + return '#fbbf24' } - return '#1a192b' + return '#4b5563' }) @@ -38,12 +37,12 @@ const bgColor = toRef(() => { -
-
- - 🚧 - 📦 -
+
+ + 🚧 + 😎 + 🚫 + 📦
@@ -51,22 +50,22 @@ const bgColor = toRef(() => { .process-node { padding: 10px; color: white; - border: 1px solid #1a192b; border-radius: 99px; - font-size: 10px; - width: 15px; - height: 15px; + font-size: 12px; + width: 18px; + height: 18px; display: flex; align-items: center; justify-content: center; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); } .spinner { - border: 2px solid #f3f3f3; - border-top: 2px solid #3498db; + border: 3px solid #f3f3f3; + border-top: 3px solid #10b981; border-radius: 50%; - width: 8px; - height: 8px; + width: 10px; + height: 10px; animation: spin 1s linear infinite; } diff --git a/docs/examples/layout/initial-elements.js b/docs/examples/layout/initial-elements.js index 84d6b8aa..80b104cd 100644 --- a/docs/examples/layout/initial-elements.js +++ b/docs/examples/layout/initial-elements.js @@ -67,6 +67,7 @@ export const initialEdges = [ { id: 'e2-2b', source: '2', target: '2b', type: 'smoothstep', animated: true }, { id: 'e2-2c', source: '2', target: '2c', type: 'smoothstep', animated: true }, { id: 'e2c-2d', source: '2c', target: '2d', type: 'smoothstep', animated: true }, + { id: 'e3-7', source: '3', target: '4', type: 'smoothstep', animated: true }, { id: 'e4-5', source: '4', target: '5', type: 'smoothstep', animated: true }, { id: 'e5-6', source: '5', target: '6', type: 'smoothstep', animated: true }, { id: 'e5-7', source: '5', target: '7', type: 'smoothstep', animated: true }, diff --git a/docs/examples/layout/useRunProcess.js b/docs/examples/layout/useRunProcess.js index b3f7163c..eb48410f 100644 --- a/docs/examples/layout/useRunProcess.js +++ b/docs/examples/layout/useRunProcess.js @@ -1,4 +1,4 @@ -import { ref } from 'vue' +import { ref, toRef, toValue } from 'vue' import { useVueFlow } from '@vue-flow/core' /** @@ -9,80 +9,112 @@ import { useVueFlow } from '@vue-flow/core' * * When a node has multiple descendants, it will run them in parallel. */ -export function useRunProcess() { +export function useRunProcess(dagreGraph) { const { updateNodeData } = useVueFlow() - const running = ref(false) - const executedNodes = new Set() + const graph = toRef(() => toValue(dagreGraph)) - async function runNode(node, dagreGraph) { + const isRunning = ref(false) + const executedNodes = new Set() + const runningTasks = new Map() + + async function runNode(node) { if (executedNodes.has(node.id)) { return } executedNodes.add(node.id) - updateNodeData(node.id, { isRunning: true, isFinished: false, hasError: false }) + updateNodeData(node.id, { isRunning: true, isFinished: false, hasError: false, isCancelled: false }) // Simulate an async process with a random timeout between 1 and 3 seconds const delay = Math.floor(Math.random() * 2000) + 1000 - await new Promise((resolve) => setTimeout(resolve, delay)) + return new Promise((resolve) => { + const timeout = setTimeout(async () => { + const children = graph.value.successors(node.id) - const children = dagreGraph.successors(node.id) + // Randomly decide whether the node will throw an error + const willThrowError = Math.random() < 0.15 - // Randomly decide whether the node will throw an error - const willThrowError = Math.random() < 0.15 + if (willThrowError) { + updateNodeData(node.id, { isRunning: false, hasError: true }) - if (willThrowError) { - updateNodeData(node.id, { isRunning: false, hasError: true }) + await skipDescendants(node.id) + runningTasks.delete(node.id) - await skipDescendants(node.id, dagreGraph) - return - } + resolve() + return + } - updateNodeData(node.id, { isRunning: false, isFinished: true }) + updateNodeData(node.id, { isRunning: false, isFinished: true }) - // Run the process on the children in parallel - await Promise.all( - children.map((id) => { - return runNode({ id }, dagreGraph) - }), - ) + runningTasks.delete(node.id) + + if (children.length > 0) { + // Run the process on the children in parallel + await Promise.all(children.map((id) => runNode({ id }))) + } + + resolve() + }, delay) + + runningTasks.set(node.id, timeout) + }) } - async function run(nodes, dagreGraph) { - if (running.value) { + async function run(nodes) { + if (isRunning.value) { return } reset(nodes) - running.value = true + isRunning.value = true // Get all starting nodes (nodes with no predecessors) - const startingNodes = nodes.filter((node) => dagreGraph.predecessors(node.id)?.length === 0) + const startingNodes = nodes.filter((node) => graph.value.predecessors(node.id)?.length === 0) // Run the process on all starting nodes in parallel - await Promise.all(startingNodes.map((node) => runNode(node, dagreGraph))) + await Promise.all(startingNodes.map(runNode)) - running.value = false - executedNodes.clear() + clear() } function reset(nodes) { + clear() + for (const node of nodes) { - updateNodeData(node.id, { isRunning: false, isFinished: false, hasError: false, isSkipped: false }) + updateNodeData(node.id, { isRunning: false, isFinished: false, hasError: false, isSkipped: false, isCancelled: false }) } } - async function skipDescendants(nodeId, dagreGraph) { - const children = dagreGraph.successors(nodeId) + async function skipDescendants(nodeId) { + const children = graph.value.successors(nodeId) for (const child of children) { updateNodeData(child, { isRunning: false, isSkipped: true }) - await skipDescendants(child, dagreGraph) + await skipDescendants(child) } } - return { run, running } + function stop() { + isRunning.value = false + + for (const [nodeId, task] of runningTasks) { + clearTimeout(task) + runningTasks.delete(nodeId) + updateNodeData(nodeId, { isRunning: false, isFinished: false, hasError: false, isSkipped: false, isCancelled: true }) + skipDescendants(nodeId) + } + + executedNodes.clear() + } + + function clear() { + isRunning.value = false + executedNodes.clear() + runningTasks.clear() + } + + return { run, stop, reset, isRunning } } diff --git a/examples/vite/src/Layouting/LayoutingExample.vue b/examples/vite/src/Layouting/LayoutingExample.vue index d6707b0f..32a6f706 100644 --- a/examples/vite/src/Layouting/LayoutingExample.vue +++ b/examples/vite/src/Layouting/LayoutingExample.vue @@ -1,9 +1,6 @@ @@ -26,12 +26,12 @@ const bgColor = toRef(() => { -
-
- - 🚧 - 📦 -
+
+ + 🚧 + 😎 + 🚫 + 📦
@@ -39,22 +39,22 @@ const bgColor = toRef(() => { .process-node { padding: 10px; color: white; - border: 1px solid #1a192b; + border: 1px solid #4b5563; border-radius: 99px; - font-size: 10px; - width: 15px; - height: 15px; + font-size: 12px; + width: 18px; + height: 18px; display: flex; align-items: center; justify-content: center; } .spinner { - border: 2px solid #f3f3f3; - border-top: 2px solid #3498db; + border: 3px solid #f3f3f3; + border-top: 3px solid #10b981; border-radius: 50%; - width: 8px; - height: 8px; + width: 10px; + height: 10px; animation: spin 1s linear infinite; } diff --git a/examples/vite/src/Layouting/initial-elements.ts b/examples/vite/src/Layouting/initial-elements.ts index 962746cc..ee7af56b 100644 --- a/examples/vite/src/Layouting/initial-elements.ts +++ b/examples/vite/src/Layouting/initial-elements.ts @@ -6,7 +6,6 @@ const type: string = 'process' export const initialNodes: Node[] = [ { id: '1', - label: 'Start', position, type, }, @@ -69,6 +68,7 @@ export const initialEdges: Edge[] = [ { id: 'e2-2b', source: '2', target: '2b', type: 'smoothstep', animated: true }, { id: 'e2-2c', source: '2', target: '2c', type: 'smoothstep', animated: true }, { id: 'e2c-2d', source: '2c', target: '2d', type: 'smoothstep', animated: true }, + { id: 'e3-7', source: '3', target: '4', type: 'smoothstep', animated: true }, { id: 'e4-5', source: '4', target: '5', type: 'smoothstep', animated: true }, { id: 'e5-6', source: '5', target: '6', type: 'smoothstep', animated: true }, { id: 'e5-7', source: '5', target: '7', type: 'smoothstep', animated: true }, diff --git a/examples/vite/src/Layouting/useRunProcess.ts b/examples/vite/src/Layouting/useRunProcess.ts index add31528..960c8f9c 100644 --- a/examples/vite/src/Layouting/useRunProcess.ts +++ b/examples/vite/src/Layouting/useRunProcess.ts @@ -1,5 +1,7 @@ +import type dagre from 'dagre' import type { Node } from '@vue-flow/core' import { useVueFlow } from '@vue-flow/core' +import type { MaybeRefOrGetter } from 'vue' /** * Composable to simulate running a process tree. @@ -9,80 +11,112 @@ import { useVueFlow } from '@vue-flow/core' * * When a node has multiple descendants, it will run them in parallel. */ -export function useRunProcess() { +export function useRunProcess(dagreGraph: MaybeRefOrGetter) { const { updateNodeData } = useVueFlow() - const running = ref(false) - const executedNodes = new Set() + const graph = toRef(() => toValue(dagreGraph)) - async function runNode(node: { id: string }, dagreGraph: dagre.graphlib.Graph) { + const isRunning = ref(false) + const executedNodes = new Set() + const runningTasks = new Map() + + async function runNode(node: { id: string }) { if (executedNodes.has(node.id)) { return } executedNodes.add(node.id) - updateNodeData(node.id, { isRunning: true, isFinished: false, hasError: false }) + updateNodeData(node.id, { isRunning: true, isFinished: false, hasError: false, isCancelled: false }) // Simulate an async process with a random timeout between 1 and 3 seconds const delay = Math.floor(Math.random() * 2000) + 1000 - await new Promise((resolve) => setTimeout(resolve, delay)) + return new Promise((resolve) => { + const timeout = setTimeout(async () => { + const children = graph.value.successors(node.id) as unknown as string[] - const children = dagreGraph.successors(node.id) as unknown as string[] + // Randomly decide whether the node will throw an error + const willThrowError = Math.random() < 0.15 - // Randomly decide whether the node will throw an error - const willThrowError = Math.random() < 0.15 + if (willThrowError) { + updateNodeData(node.id, { isRunning: false, hasError: true }) - if (willThrowError) { - updateNodeData(node.id, { isRunning: false, hasError: true }) + await skipDescendants(node.id) + runningTasks.delete(node.id) - await skipDescendants(node.id, dagreGraph) - return - } + resolve() + return + } - updateNodeData(node.id, { isRunning: false, isFinished: true }) + updateNodeData(node.id, { isRunning: false, isFinished: true }) + console.log(`Node ${node.id} finished`) + runningTasks.delete(node.id) - // Run the process on the children in parallel - await Promise.all( - children.map((id) => { - return runNode({ id }, dagreGraph) - }), - ) + if (children.length > 0) { + // Run the process on the children in parallel + await Promise.all(children.map((id) => runNode({ id }))) + } + + resolve() + }, delay) + + runningTasks.set(node.id, timeout) + }) } - async function run(nodes: Node[], dagreGraph: dagre.graphlib.Graph) { - if (running.value) { + async function run(nodes: Node[]) { + if (isRunning.value) { return } reset(nodes) - running.value = true + isRunning.value = true // Get all starting nodes (nodes with no predecessors) - const startingNodes = nodes.filter((node) => dagreGraph.predecessors(node.id)?.length === 0) + const startingNodes = nodes.filter((node) => graph.value.predecessors(node.id)?.length === 0) // Run the process on all starting nodes in parallel - await Promise.all(startingNodes.map((node) => runNode(node, dagreGraph))) + await Promise.all(startingNodes.map(runNode)) - running.value = false - executedNodes.clear() + clear() } function reset(nodes: Node[]) { + clear() + for (const node of nodes) { - updateNodeData(node.id, { isRunning: false, isFinished: false, hasError: false, isSkipped: false }) + updateNodeData(node.id, { isRunning: false, isFinished: false, hasError: false, isSkipped: false, isCancelled: false }) } } - async function skipDescendants(nodeId: string, dagreGraph: dagre.graphlib.Graph) { - const children = dagreGraph.successors(nodeId) as unknown as string[] + async function skipDescendants(nodeId: string) { + const children = graph.value.successors(nodeId) as unknown as string[] for (const child of children) { updateNodeData(child, { isRunning: false, isSkipped: true }) - await skipDescendants(child, dagreGraph) + await skipDescendants(child) } } - return { run, running } + function stop() { + isRunning.value = false + + for (const [nodeId, task] of runningTasks) { + clearTimeout(task) + runningTasks.delete(nodeId) + updateNodeData(nodeId, { isRunning: false, isFinished: false, hasError: false, isSkipped: false, isCancelled: true }) + skipDescendants(nodeId) + } + + executedNodes.clear() + } + + function clear() { + isRunning.value = false + executedNodes.clear() + runningTasks.clear() + } + + return { run, stop, reset, isRunning } }