English | δΈζ
- Fully DOM based auto-scheduling flowchart component. (While not as free to drag and drop as the canvas implementation, it also retains the rendering and event capabilities of the DOM.)
- Highly customizable.
- it's lightweight. 14.5kb uncompressed, 3.6kb after compression (gzip).
I don't know what the use is. I think even if it could be used somewhere, it would be very scenario-specific and customizable. So rather than presenting it in the documentation from the perspective of a component, I'd prefer to talk about the implementation idea. Any suggestions or questions about self-contained scenes are welcome to be discussed in the issue.
interface NodeData {
id: string;
next?: string[];
pre?: string[];
[others: string]: any;
}
const data: Record<string, NodeData> = {
head: {
id: 'head',
next: ['tail'],
type: 'start',
},
tail: {
id: 'tail',
pre: ['head'],
type: 'end',
}
}
data is a chained object, with no pre at the head node and no next at the tail node.
There are two types of component structures, Section and Node, which are mutually referenced and nested. Each Section is a wrapping container for a set of sibling nodes. Each Node is a rendering container for a node. If a Branch is created at a Section, i.e., it contains >= 2 Nodes, it will also contain the Section of the branch closing node. If the node represented by a Node has a successor node, and the successor node is not a branch closure node, then the Node will also contain a Section containing the successor node.
function Section() {
return (
<>
{nodes.map(node =>
<Node {...node} />
)}
{isBranchSection &&
<Section {...combineNode} />
}
</>
);
}
function Node() {
return (
<>
<NodeRender />
{nextNode && nextNodeNotCombineNode &&
<Section {...nextNode} />
}
</>
);
}
Each black boxed area in the figure below is a Section component.
Each red boxed area in the figure below is a Node component.
This nested structure also poses a problem: Strict Mode needs to be turned off for use in dev environment. hooks in Strict Mode will mount to incorrect component.
The vertical line is relatively simple, using the preset height and the height of the DOM in the middle of the Section, Node on the line.
The horizontal lines generated by the branch nodes belong to the Section, and the Node at the head and tail need to be covered with a line of the same thickness in the background color, as shown in the figure below.
There are many lines, if you do not feel the right use, debug the code when commented out to see what will change.
The above illustrates how the flowchart is laid out and arranged. As an interactive flowchart, you also need to have buttons to click to add nodes. The following three buttons are the ones that I think are mandatory for flowcharts and have little to do with what kind of nodes I want to render, so they are preset in the component.
- An Add button attributed to a Node that triggers an onAdd callback when clicked.
- Branch button for adding branches, click to trigger the onExpandBranch callback.
- Combine button for adding branch merge nodes, click to trigger onCombineNodes callback.
The button used to delete the node is not preset, and the callback onDeleteNode is obtained in the node's custom render function.
interface Callback {
onAdd(nodeId: string): void;
onExpandBranch(nodeId: string): void;
onCombineNodes(nodesId: Array<string>): void;
onDeleteNode(nodeId: string): void;
}
npm install --save custom-react-flowchart
// import style then
import 'custom-react-flowchart/dist/style.css';
To use this flowchart, you first need a data source that conforms to the rules, and then define callbacks for the three button operations. Then write a node rendering function RenderNode, RenderNode function will receive the callback function onDeleteNode for the delete operation. delete button will have to be written in RenderNode itself.
custom-react-flowchart only cares about the layout, not how the data is manipulated, as long as the data is conforming to the rules. Of course a set of methods for manipulating data is also provided, see the code of the demo for details.
function flowchart () {
const [data, setData] = useState({
head: {
id: 'head',
next: ['tail'],
type: 'start',
},
tail: {
id: 'tail',
pre: ['head'],
type: 'end',
}
});
return (
<FlowChart
data={data}
onAdd={onHandleAdd}
onCombineNodes={onHandleCombineNodes}
onExpandBranch={onHandleExpandBranch}
styleConfig={{
backgroundColor: '#fff',
lineWidth: 1,
lineColor: '#ccc',
distance: { horizontal: 50, vertical: 40 },
}}
RenderNode={RenderNode}
onDeleteNode={onHandleDeleteNode}
/>
);
}
data (required) :object
The head node does not have pre and the tail node does not have next.
interface NodeData {
id: string;
next?: string[];
pre?: string[];
[others: string]: any;
}
type data = Record<string, NodeData>;
styleConfig :object
interface StyleConfig {
/** Flowchart background color for masking horizontal line. */
backgroundColor?: string;
lineWidth?: number;
lineColor?: string;
distance?: { horizontal: number, vertical: number };
}
RenderNode (required) :ReactNode
The render function for the node. The node's properties and the onDeleteNode callback can be received.
onAdd (required) (nodeId: string): void
Callback for clicking the Add button
onExpandBranch (required) (nodeId: string): void
Callback for clicking the Branch button
onCombineNodes (required) (nodesId: Array<string>): void
Callback for clicking the Combine button
onDeleteNode (required) (nodeId: string): void
The delete callback passed through to the RenderNode for the custom delete button.
RenderAddButton :ReactNode
The custom rendering function for the Add button.
RenderBranchButton :ReactNode
The custom rendering function for the Branch button.
RenderCombineButton :ReactNode
The custom rendering function for the Combine button.
showAddButton :boolean
= true
Indicates if the Add button is displayed.
showBranchButton :boolean
= true
Indicates if the Branch button is displayed.
showCombineButton :boolean
= true
Indicates if the Combine button is displayed.
Run the demos:
npm install
npm run start
npm install
npm run build:lib
MIT