Use the buttons above the chart to see different sets of aggregated links.
This chart shows a supply chain where goods are transported using three transport methods - road freight, air freight and rail freight.
Aggregate By Transport uses the aggregateBy property to aggregate links showing the same method of transport, while All aggregates all the links at each step of the supply chain.
You can also add styling or summary information on aggregated links using the setStyle function passed to the onLinkAggregation event handler.
Link aggregation is currently in beta.
See also
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { Chart } from "regraph";
import { items } from "./data";
const aggregateLinksOptions = [false, true, { aggregateBy: "via" }];
const aggregateLinksButton = ["None", "All", "Aggregate By Transport"];
const layout = { name: "sequential", orientation: "right", tightness: 2 };
function Basics() {
const [state, setState] = useState({
openCombos: {},
combine: {
level: 2,
properties: ["level1", "level2"],
shape: "rectangle",
},
aggregateLinks: aggregateLinksOptions[2],
});
const doubleClickHandler = ({ id }) => {
setState((current) => {
return {
...current,
openCombos: { ...current.openCombos, [id]: !current.openCombos[id] },
combine: { ...current.combine },
};
});
};
const aggregateLinksHandler = (props) => {
const { links: linkDict, aggregateByValue, setStyle } = props;
const links = Object.values(linkDict);
const color = aggregateByValue ? links[0].color : "grey";
const glyph = links[0].glyphs[0];
glyph.backgroundColor = color;
const style = {
width: links.length,
color,
glyphs: [
aggregateByValue ? glyph : undefined,
{
label: { text: links.length, bold: true, color },
color: "white",
border: { color },
size: 1.25,
},
],
};
setStyle(style);
};
return (
<div className="story">
<div className="options">
<span className="label">Link Aggregation </span>
{aggregateLinksOptions.map((value, index) => {
const valueStr = JSON.stringify(value);
const aggregateLinksStr = JSON.stringify(state.aggregateLinks);
return (
<button
key={index}
type="button"
className={valueStr === aggregateLinksStr ? "active" : ""}
onClick={() => setState({ ...state, aggregateLinks: value })}
>
{aggregateLinksButton[index]}
</button>
);
})}
</div>
<Chart
items={items}
options={{
iconFontFamily: "Font Awesome 5 Free",
navigation: false,
overview: false,
selection: false,
links: { avoidLabels: false },
}}
layout={layout}
aggregateLinks={state.aggregateLinks}
onLinkAggregation={aggregateLinksHandler}
onDoubleClick={doubleClickHandler}
combine={state.combine}
onCombineNodes={({ setStyle, id, nodes, combo }) => {
const { closedStyle, openStyle } = createComboStyling(nodes, combo);
setStyle({
open: !!state.openCombos[id],
closedStyle,
...openStyle,
});
}}
onCombineLinks={({ setStyle }) => {
setStyle({ contents: true, summary: false });
}}
/>
</div>
);
}
function createComboStyling(nodes, combo) {
const labelText = combo.level1 || combo.level2;
const childNode = Object.values(nodes)[0];
const backgroundColor = "#2dcda8";
const color = "white";
const padding = "4 6";
const border = { radius: 10 };
const closedStyle = {
...childNode,
label: [
childNode.label[0],
{
text: labelText,
color,
position: "s",
backgroundColor,
border,
padding,
},
],
};
const openStyle = {
arrange: "grid",
gridShape: { columns: 1 },
border: { color: childNode.border.color },
color: childNode.color,
label: {
text: labelText,
backgroundColor,
color,
border,
padding,
margin: 5,
position: "s",
},
};
return { closedStyle, openStyle };
}
const FontReadyChart = React.lazy(() =>
document.fonts.load("900 24px 'Font Awesome 5 Free'").then(() => ({
default: Basics,
}))
);
export function Demo() {
return (
<React.Suspense fallback="">
<FontReadyChart />
</React.Suspense>
);
}
const root = createRoot(document.getElementById("regraph"));
root.render(<Demo />); function createNode(icon, text, data) {
return {
border: { color: "#2dcda8" },
color: "white",
label: [
{
fontIcon: {
text: icon,
color: "#2dcda8",
},
fontSize: 40,
},
{
text,
color: "white",
position: "s",
backgroundColor: "#2dcda8",
border: { radius: 10 },
padding: "4 6",
},
],
data,
};
}
function createLink(id1, id2, iconText, color, via) {
return {
id1,
id2,
color,
glyphs: [
{
fontIcon: {
text: iconText,
color,
},
color: "white",
border: { color },
size: 1.25,
},
],
end2: { arrow: true },
data: { via },
};
}
function createPlaneLink(id1, id2) {
return createLink(id1, id2, "fas fa-plane", "#3377ff", "air");
}
function createTruckLink(id1, id2) {
return createLink(id1, id2, "fas fa-truck", "#dd3c3c", "road");
}
function createTrainLink(id1, id2) {
return createLink(id1, id2, "fas fa-train", "#048170", "rail");
}
export const items = {
// nodes
//factory
"assembly-line-1": createNode("fas fa-industry", "Assembly Line 1", { level1: "Factory" }),
"assembly-line-2": createNode("fas fa-industry", "Assembly Line 2", { level1: "Factory" }),
// storage
"warehouse-1-container-1": createNode("fas fa-warehouse", "Container 1", {
level2: "Warehouses",
level1: "Warehouse 1",
}),
"warehouse-1-container-2": createNode("fas fa-warehouse", "Container 2", {
level2: "Warehouses",
level1: "Warehouse 1",
}),
"warehouse-1-container-3": createNode("fas fa-warehouse", "Container 3", {
level2: "Warehouses",
level1: "Warehouse 1",
}),
"warehouse-2-container-1": createNode("fas fa-warehouse", "Container 1", {
level2: "Warehouses",
level1: "Warehouse 2",
}),
"warehouse-2-container-2": createNode("fas fa-warehouse", "Container 2", {
level2: "Warehouses",
level1: "Warehouse 2",
}),
"warehouse-2-container-3": createNode("fas fa-warehouse", "Container 3", {
level2: "Warehouses",
level1: "Warehouse 2",
}),
"warehouse-2-container-4": createNode("fas fa-warehouse", "Container 4", {
level2: "Warehouses",
level1: "Warehouse 2",
}),
// store
store: createNode("fas fa-store", "Retail Outlet"),
// links
// assembly lines to containers
"assembly-line-1-warehouse-1-container-1": createPlaneLink(
"assembly-line-1",
"warehouse-1-container-1"
),
"assembly-line-1-warehouse-1-container-2": createTruckLink(
"assembly-line-1",
"warehouse-1-container-2"
),
"assembly-line-1-warehouse-1-container-3": createTruckLink(
"assembly-line-1",
"warehouse-1-container-3"
),
"assembly-line-2-warehouse-2-container-1": createTrainLink(
"assembly-line-2",
"warehouse-2-container-1"
),
"assembly-line-2-warehouse-2-container-2": createTrainLink(
"assembly-line-2",
"warehouse-2-container-2"
),
"assembly-line-2-warehouse-2-container-3": createTruckLink(
"assembly-line-2",
"warehouse-2-container-3"
),
"assembly-line-2-warehouse-2-container-4": createTruckLink(
"assembly-line-2",
"warehouse-2-container-4"
),
// containers to store
"warehouse-1-container-1-store": createTruckLink("warehouse-1-container-1", "store"),
"warehouse-1-container-2-store": createTrainLink("warehouse-1-container-2", "store"),
"warehouse-1-container-3-store": createTrainLink("warehouse-1-container-3", "store"),
"warehouse-2-container-1-store": createTruckLink("warehouse-2-container-1", "store"),
"warehouse-2-container-2-store": createTruckLink("warehouse-2-container-2", "store"),
"warehouse-2-container-3-store": createTrainLink("warehouse-2-container-3", "store"),
"warehouse-2-container-4-store": createTrainLink("warehouse-2-container-4", "store"),
}; <!doctype html>
<html>
<body>
<div id="regraph" style="height: 100vh"></div>
<script type="module" src="./code.jsx"></script>
</body>
</html>