Skip to main content

Sub-DAG (Node Group)

Sub DAG means a node render can be a DAG view which is generated by Nice-DAG as well. Here is to give a sample which can explain how to generate a sub DAG. To simply the illustratation, the example extends Read-Only DAG.

Step 1: Adapt Node List

Sub-DAG can be regarded as a group of nodes. Before the render adaption, you need to know how a data structure can be represented a node group. There are two ways to define a node group. modelType of useNiceDag is used to indicated which structure should be used.

Hierarchical Structure

const NodeData = [
{
id: "start",
},
{
id: "task",
dependencies: ["start"],
children: [ //children is an array of nodes which can represent a node group
{
id: "sub-task-1",
},
{
id: "sub-task-2",
dependencies: ["sub-task-1"],
},
]
},
{
id: "end",
dependencies: ["task"],
},
];

In this example, the task is a node which contains a sub DAG view because the node has a children definition with an array.

If you use the hierarchical structure, you don't need any change of useNiceDag parameters because modelType is HIERARCHY in default.

Flatten Structure

const NodeData = [
{
id: "start",
},
{
id: "task",
dependencies: ["start"],
},
{
id: "sub-task1",
parentId: "task",
},
{
id: "sub-task2",
parentId: "task",
dependencies: ["sub-task1"],
},
{
id: "end",
dependencies: ["task"],
},
];

In this example, the task is a node which contains a sub DAG view. The difference between flatten structure and hierarchical structure is that the flatten one uses parentId to grouping nodes. In this sample, sub-task1 and sub-task2 have the same parentId task so that they are in the same group.

Notes: Whether to use hierarchical structure or flatten structure, the node id MUST be unique. This is due to the renderNode function is unaware of the node layers. It is called by a React.createPortal component which is associated to a DOM node by the unique id.

If you use the flatten structure, you don't need any change of useNiceDag parameters but set modelType to FLATTEN.

export function MyFirstDag() {
//call useNiceDag
const { niceDagEl, render } = useNiceDag({
initNodes: NodeData,
modelType: 'FLATTEN'
});

...
}

Step 2: Adapt the node render

The size of a node group isn't controlled by getNodeSize because it is determined by the content (children) of the node. Usually, you can call useNiceDag with a parameter subViewPadding object for a node group

Nice-DAG gives a default sub DAG padding object.

{
top: 40,
bottom: 20,
left: 20,
right: 20,
}

For this example, we can leave the default value. Once the node group has the paddings, you can leverage the padding to add some DOM nodes (e.g. button, <a/>) to control the group.

function GroupControl({ node }) {
const onClick = useCallback(()=>{
node.shrink(); //to shrink the group
},[node]);
return (
<div className="my-first-dag-node-group-content">
<div>
{node.id}
<button onClick={onClick}>-</button>
</div>
</div>
);
}

function NodeControl({ node }) {
const onClick = useCallback(()=>{
node.expand(); //to expand the group
},[node]);
return (
<div className="my-first-dag-node-group-content">
<div>
{node.id}
{node.children?.length > 0 && <button onClick={onClick}>+</button>}
</div>
</div>
);
}

function SampleNode({ node, niceDag }) {
return (
<div className="my-first-dag-node">
{node.children?.length > 0 && !node.collapse ? (
<GroupControl node={node} niceDag={niceDag} />
) : (
<NodeControl node={node} niceDag={niceDag} />
)}
</div>
);
}

Notes Whether to use hierarchical structure of flatten structure, the node passed by renderNode function uses children to indicate if it is a node group. This is due to Node objects is converted or IViewNode objects once it sets to the useNiceDag.