Based on
Forked and updated by Seiko Labs team: @zdllucky
- Added context action buttons for cards (Top right corner). Data to resolve can be accessed through
node.actions.data
object. See node config example:
const exampleNodeConfig = {
// Other node config
actions: {
// Additional node data
data: {
breakpoint: false,
},
// Node upper actions
buttons: [
(
actionsData, // Node editable data
actionsDispatch, // Data update method, e.g. actionsDispatch(actionsData => {...actionsData})
inputData, // Node ports data
connections, // Node connections
nodeData, // Node data, including actions data
nodesDispatch // Basic node dispatch, still unstable
) => (
<Breakpoint
active={actionsData.breakpoint}
onClick={() =>
actionsDispatch((data) => ({
...data,
breakpoint: !data.breakpoint,
}))
}
/>
),
],
},
};
const resolveExampleNodeConfig = (node, inputValues, ...other) => ({
...inputValues,
breakpoint: node.actions.data.breakpoint,
});
-
New
validate: (v) => boolean
option for text and number controls validation -
New
button
Control added. The API is as follows:
Controls.button({
type: "button",
name: "button",
defaultValue: undefined,
label: "",
/**
* @param {Object} inputData
* @param {Object} nodeData
* @param {(newData: !Object, controlName: string, portName: string, nodeId: string) => void} changeData - changes node inputData
* @param {Object} context
* @param {() => void} redraw
* @void
*/
onPress: (inputData, nodeData, changeData, context, redraw) => {},
});
-
New option
openEditor
. This callback fires onControls.text
enlarge button click. -
Big UI design update
-
New stage controls
-
New port API forces using
hidePort: true
option on every input port that accept connections -
Node builder
tileFontColor
(Web Color) andtileBackground
(Web Color) are now wrapped insidecategory
(json) parameter within additionalid
(number),label
(string) anddescription
(string) props representing node type category name. -
Node builder now accepts and contains additional
icon
(URL string) andexpanded
(boolean) parameters, that define card layout and view. -
Added
NodeEditor
state init params touseNodeEditorController
hook params. It now accepts:
{
(initialNodesState = null), // State of nodes containing steps state and actions
(initialTempState = {
// Initial temp state
multiselect: false, // Multiselect
selectedNodes: [], // Selected nodes
stage: {
// Stage state
scale: 1, // Zoom factor
translate: {
// Stage disposition (x, y coords)
x: 0,
y: 0,
},
},
}),
(initialNodes = null), // Nodes to be placed by default (Optional)
(defaultNodes = null), // Default nodes (Optional)
(options = {
openEditor(data, onChange, nodeData) {},
});
}
-
Reversed input and output IO pins. Was done so to let
rootNode
construct LTR -
Added
useNodeEditorController
controller hook for deeperNodeEditor
management. This concept revokes direct nodes control viaNodeEditor
props in labor to action dispatching. This controller hook also has two types of dispatch functions. See code for more explanation -
New features:
-
Highlighting nodes. See
"HIGHLIGHT_NODE"
action ofuseNodeEditorController
dispatch()
function -
Adding new nodes. See
"ADD_NODE"
action ofuseNodeEditorController
dispatch()
function -
Nodes selection / multi selection. See
"TOGGLE_MULTISELECT"
temp action dispatch function -
Copy / cut / paste actions with external API. The localstorage is used as a buffer See
COPY
,CUT
,PASTE
actions ofuseNodeEditorController
dispatch()
function -
Undo / redo actions. See
UNDO
,REDO
actions ofuseNodeEditorController
dispatch function -
Optional node ports toggling. See
TOGGLE_NODES_VIEW
action ofuseNodeEditorController
dispatch function
-
Initial GitHub repository: flume.dev
yarn add https://github.com/Seiko-Labs/flume # Latest stable branch
yarn add https://github.com/Seiko-Labs/flume#dev # Feature preview branch
Import FlumeConfig
and use it to define the nodes and ports that will make up your node editor.
import { FlumeConfig, Controls, Colors } from "flume";
const flumeConfig = new FlumeConfig();
const numberCategory = {
id: 1,
label: "Numbers",
description: "Number actions will appear here",
titleColor: "#ccc",
tileBackground: "#FF45D3",
};
flumeConfig
.addPortType({
type: "number",
name: "number",
label: "Number",
color: Colors.red,
controls: [
Controls.number({
name: "num",
label: "Number",
}),
],
})
.addNodeType({
type: "number",
label: "Number",
initialWidth: 150,
inputs: (ports) => [ports.number()],
outputs: (ports) => [ports.number()],
})
.addNodeType({
type: "addNumbers",
label: "Add Numbers",
initialWidth: 150,
category: numberCategory,
inputs: (ports) => [
ports.number({ name: "num1" }),
ports.number({
name: "num2",
// This parameter makes port data collapsible,
// Works only with [hidePort: true]
optional: true,
}),
],
outputs: (ports) => [ports.number({ name: "result" })],
});
To render the node editor, import NodeEditor
and pass it your nodeTypes and portTypes from the configuration you
created.
import React from "react";
import { NodeEditor, useNodeEditorController } from "flume";
import config from "./config";
import styled from "styled-components";
const nodeEditorStateData = {
initialNodesState: null,
initialTempState: {
multiselect: false,
selectedNodes: [],
stage: {
scale: 1,
translate: {
x: 0,
y: 0,
},
},
},
initialNodes: null,
defaultNodes: null,
};
const App = () => {
// This is a controller hook that is used to
// dispatch actions to the NodeEditor watch contents ans so on...
//
// Note: There is a difference between nodes + dispatch and
// temp reducers: former sends data directly to the NodeEditor root
// state, unlike temp only contains view action type modifiers
const [
{ nodesState, currentStateIndex }, // Contains NodeEditor's current nodesState and state index
comments, // Contains NodeEditor's current comments
dispatch, // This method dispatches actions to NodeEditor
connector, // Links editor to the controller
temp, // This reducer contains additionals to interact with NodeEditor
] = useNodeEditorController(nodeEditorStateData);
return (
<div style={{ width: "100vw", height: "100vh" }}>
<ControlsBlock>
{/* Undo changes */}
<button onClick={() => dispatch("UNDO")}>Undo</button>
{/* Redo changes */}
<button onClick={() => dispatch("REDO")}>Redo</button>
{/* Copy selected nodes */}
<button onClick={() => dispatch("COPY")}>Copy</button>
{/* Cut selected nodes */}
<button onClick={() => dispatch("CUT")}>Cut</button>
{/* Paste copied nodes */}
<button onClick={() => dispatch("PASTE")}>Paste</button>
{/* Expand nodes' optional fileds */}
<button
onClick={() =>
dispatch("TOGGLE_NODES_VIEW", {
nodeIds: Object.keys(nodes),
doExpand: true,
})
}
>
Expand all nodes
</button>
{/* Add new node */}
<button
onClick={() =>
dispatch("TOGGLE_NODES_VIEW", {
nodeIds: Object.keys(nodes),
doExpand: true,
})
}
>
Expand all nodes
</button>
{/* nodes' optional fileds */}
<button
onClick={() =>
dispatch("ADD_NODE", {
type: "addNumbers",
x: 100,
y: 200,
})
}
>
Add "addNumbers" node
</button>
<label style={{ color: "white" }}>
{/*Toggle nodes multiselect mode*/}
<input
type="checkbox"
onChange={(e) => {
temp.dispatch({
type: "TOGGLE_MULTISELECT",
doEnable: e.target.checked,
});
}}
/>
Toggle multiselect
</label>
</ControlsBlock>
<NodeEditor
nodeTypes={config.nodeTypes}
portTypes={config.portTypes}
// Link connector to the editor for further editor control
connector={connector}
defaultNodes={[
{
type: "start",
x: -410,
y: -150,
},
]}
/>
</div>
);
};
const ControlsBlock = styled.div`
position: fixed;
display: inline-block;
top: 10px;
left: 10px;
z-index: 9999;
& > * {
margin-right: 10px;
}
`;
For initial flume documentation visit: flume.dev
MIT © chrisjpatty