is a visual editor for Dataflow programming
With npm do
npm install flow-view
Try this in your HTML page
<script type="module">
import { FlowView } from "https://unpkg.com/flow-view"
const flowView = new FlowView(document.body)
</script>
Try demo here
- Drag on canvas to translate all items.
- Click on item to select it.
- Click while pressing SHIFT to enable multi selection.
- Drag selected items to translate them.
- Drag from a node output to a node input to create an edge.
- Press BACKSPACE to delete selected items.
- Double click on edge to delete it.
- Double click on canvas to open the selector.
- Type into the selector then press ENTER to create a new node.
Create a FlowView
instance and pass it a container. It will create a
flow-view
custom element and attach it to the container. Be aware that the
flow-view
custom element will fit the whole height of its container, so make
sure to style properly to avoid a zero height container.
<!doctype html>
<html>
<body>
<script type="module">
import { FlowView } from "https://unpkg.com/flow-view"
const flowView = new FlowView(document.body)
</script>
</body>
</html>
If some flow-view
custom element is already in the page, it can be passed to
the FlowView
constructor. argument.
<!doctype html>
<html>
<body>
<flow-view id="my-view"></flow-view>
<script type="module">
import { FlowView } from "https://unpkg.com/flow-view"
const flowView = new FlowView(document.getElementById("my-view"))
</script>
</body>
</html>
Optionally set color scheme. If not provided it defaults to both light and dark according to system preferences.
Light scheme.
<flow-view light></flow-view>
Dark scheme.
<flow-view dark></flow-view>
See also color schemes example.
Add a list to define which nodes are available. It is not required but it makes sense to be provided in the majority of use cases.
flowView.addNodeDefinitions({
nodes: [
{ name: "Marge", type: "parent" },
{ name: "Homer", type: "parent" },
{ name: "Bart", type: "child" },
{ name: "Lisa", type: "child" },
{ name: "Mr. Burns" }
],
types: {
parent: {
inputs: [],
outputs: [{ name: "out" }]
},
child: {
inputs: [{ name: "in1" }, { name: "in2" }],
outputs: []
}
}
})
Get flow-view node by id.
const node = flowView.node("abc")
Get flow-view edge by id.
const edge = flowView.edge("abc")
Access current flow-view graph.
console.log(flowView.graph)
Load a flow-view graph.
flowView.loadGraph({
nodes: [
{
id: "dad",
text: "Homer",
x: 60,
y: 70,
outs: [{ id: "children" }]
},
{
id: "mom",
text: "Marge",
x: 160,
y: 70,
outs: [{ id: "children" }]
},
{
id: "son",
text: "Bart",
x: 60,
y: 240,
ins: [{ id: "father" }, { id: "mother" }]
},
{
id: "daughter",
text: "Lisa",
x: 220,
y: 220,
ins: [{ id: "father" }, { id: "mother" }]
}
],
edges: [
{ from: ["dad", "children"], to: ["son", "father"] },
{ from: ["dad", "children"], to: ["daughter", "father"] },
{ from: ["mom", "children"], to: ["son", "mother"] },
{ from: ["mom", "children"], to: ["daughter", "mother"] }
]
})
Empty current graph.
flowView.clearGraph()
Delete flow-view
custom element.
flowView.destroy()
An use case for destroy()
is the following. Suppose you are using Next.js, you
need to load flow-view
with an async import into a useEffect
which needs to
return a callback to be called when component is unmounted.
This is a sample code.
import type { FlowView } from "flow-view";
import { FC, useEffect, useRef } from "react";
const MyComponent: FC = () => {
const flowViewContainerRef = useRef<HTMLDivElement | null>(null);
const flowViewRef = useRef<FlowView | null>(null);
useEffect(() => {
let unmounted = false;
const importFlowView = async () => {
if (unmounted) return;
if (flowViewContainerRef.current === null) return;
if (flowViewRef.current !== null) return;
const { FlowView } = await import("flow-view");
const flowView = new FlowView({
container: flowViewContainerRef.current,
});
flowViewRef.current = flowView;
};
importFlowView();
return () => {
unmounted = true;
if (flowViewRef.current !== null) flowViewRef.current.destroy();
};
}, [flowViewRef, flowViewContainerRef]);
return <div ref={flowViewContainerRef}></div>;
};
Create nodes and edges programmatically. See programmatic example here.
// Create two nodes.
const node1 = flowView.newNode({
text: "Hello",
ins: [{}, {}],
outs: [{ id: "output1" }],
x: 100,
y: 100,
width: 80
})
const node2 = flowView.newNode({
text: "World",
ins: [{ id: "input1" }],
width: 100,
x: 250,
y: 400
})
// Connect nodes with an edge.
flowView.newEdge({
from: [node1.id, "output1"],
to: [node2.id, "input1"]
})
Delete nodes and edges programmatically. Notice that when a node is deleted, all its connected edges are deleted too.
const nodeId = "abc"
const edgeId = "123"
flowView.deleteNode(nodeId)
flowView.deleteEdge(edgeId)
Can add custom node class. See custom node example here.
Set callback to be invoked on every view change. See demo code here.
Callback signature is ({ action, data }, info) => void
, where
- action can be
CREATE_NODE
,DELETE_NODE
, etc. - data change based on action
- info can contain
{ isLoadGraph: true }
or other optional information.
Set a function that will be invoked on node creation to resolve node type from node text.