Here’s what I came up with by parsing the SVG and guessing about the group memberships.
I find that the performance of collapsing and expanding groups in v3.0 is good. I’m still wondering how this is different from your situation.
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<button onclick="myDiagram.layoutDiagram(true)">Layout</button>
<button onclick="collapse()">Collapse All</button>
<button onclick="expand()">Expand All</button>
The model in JSON format:
<textarea id="mySavedModel" style="width:100%;height:250px">
{ "class": "GraphLinksModel",
"linkFromPortIdProperty": "fpid",
"linkToPortIdProperty": "tpid",
"nodeDataArray": [
{"key":-28},
{"key":"gr-5","isGroup":true},
{"key":"gr-9","isGroup":true},
{"key":"gr-3","isGroup":true},
{"key":"qqqtipxwtir","group":"gr-3"},
{"key":"gr-6","isGroup":true},
{"key":"ncivxmhbezjldeviucyci","group":"gr-6"},
{"key":"gr-7","isGroup":true},
{"key":"gr-4","isGroup":true},
{"key":"zaqirhwvvcirmlhdtba","group":"gr-4"},
{"key":"gr-8","isGroup":true},
{"key":"prslehdaftrcedkyxyfjrnwmybaxmbjbnehh","group":"gr-8"},
{"key":"gr-11","isGroup":true},
{"key":"gr-12","isGroup":true},
{"key":"vkhhwcw","group":"gr-12"},
{"key":"upzkhyiwyouvvenhzmwgxwf","group":"gr-12"},
{"key":"drydwhyh","group":"gr-12"},
{"key":"xhtttnsdkrmzpod","group":"gr-12"},
{"key":"ezhnam","group":"gr-12"},
{"key":"jqxjtuc","group":"gr-12"},
{"key":"fzgvx","group":"gr-12"},
{"key":"hiwhmkscwbengwntw","group":"gr-12"},
{"key":"gr-14","isGroup":true},
{"key":"mjec","group":"gr-14"},
{"key":"ooezdivexhsrpguriuki","group":"gr-14"},
{"key":"sfjnanljslirukwmevb","group":"gr-14"},
{"key":"gr-14-1051","isGroup":true},
{"key":"gr-31","isGroup":true},
{"group":"gr-31","key":-29},
{"group":"gr-31","key":-30}
],
"linkDataArray": [
{"from":"norrrgdpqjbvnlrwwtukuaa","fpid":"B","to":"ncivxmhbezjldeviucyci","tpid":"T"},
{"from":"norrrgdpqjbvnlrwwtukuaa","fpid":"B","to":"vkhhwcw","tpid":"T"},
{"from":"norrrgdpqjbvnlrwwtukuaa","fpid":"E","to":"mgdoxemspvtpmwyoldhyhpnwsfz","tpid":"T"},
{"from":"jpkzlxxgodk","fpid":"B","to":"ezhnam","tpid":"T"},
{"from":"joyqwwzdnrzihdbmpcgzoq","fpid":"B","to":"drydwhyh","tpid":"T"},
{"from":"ncivxmhbezjldeviucyci","fpid":"B","to":"qqqtipxwtir","tpid":"T"},
{"from":"ncivxmhbezjldeviucyci","fpid":"B","to":"mjec","tpid":"T"},
{"from":"gfiybttxrdbcieo","fpid":"B-strCanadianRetail","to":"gubfvbudrwumtsstzyics","tpid":"T"},
{"from":"gfiybttxrdbcieo","fpid":"B-strCanadianCommercial","to":"kbhziivaetyrowglauvmyvozmhi","tpid":"T"},
{"from":"gfiybttxrdbcieo","fpid":"B-strUsRetail","to":"mftvqcfgkxygzsrqe","tpid":"T"},
{"from":"gfiybttxrdbcieo","fpid":"B-strUsCommercial","to":"sfrsrvxetarskyvb","tpid":"T"},
{"from":"zaqirhwvvcirmlhdtba","fpid":"B","to":"gfiybttxrdbcieo","tpid":"T"},
{"from":"zaqirhwvvcirmlhdtba","fpid":"E","to":"prslehdaftrcedkyxyfjrnwmybaxmbjbnehh","tpid":"T"},
{"from":"kbhziivaetyrowglauvmyvozmhi","fpid":"B","to":"ncivxmhbezjldeviucyci","tpid":"T"},
{"from":"kbhziivaetyrowglauvmyvozmhi","fpid":"B","to":"vkhhwcw","tpid":"T"},
{"from":"kbhziivaetyrowglauvmyvozmhi","fpid":"E","to":"mgdoxemspvtpmwyoldhyhpnwsfz","tpid":"T"},
{"from":"prslehdaftrcedkyxyfjrnwmybaxmbjbnehh","fpid":"B","to":"joyqwwzdnrzihdbmpcgzoq","tpid":"T"},
{"from":"npdefvoijlldgctszobocrymfkr","fpid":"B","to":"joyqwwzdnrzihdbmpcgzoq","tpid":"T"},
{"from":"mftvqcfgkxygzsrqe","fpid":"B","to":"vkhhwcw","tpid":"T"},
{"from":"mftvqcfgkxygzsrqe","fpid":"B","to":"ncivxmhbezjldeviucyci","tpid":"T"},
{"from":"mftvqcfgkxygzsrqe","fpid":"E","to":"mgdoxemspvtpmwyoldhyhpnwsfz","tpid":"T"},
{"from":"ulyxsahreacdhwllzwfeb","fpid":"B","to":"ncivxmhbezjldeviucyci","tpid":"T"},
{"from":"ulyxsahreacdhwllzwfeb","fpid":"B","to":"vkhhwcw","tpid":"T"},
{"from":"ulyxsahreacdhwllzwfeb","fpid":"E","to":"mgdoxemspvtpmwyoldhyhpnwsfz","tpid":"T"},
{"from":"upzkhyiwyouvvenhzmwgxwf","fpid":"B","to":"zaqirhwvvcirmlhdtba","tpid":"T"},
{"from":"upzkhyiwyouvvenhzmwgxwf","fpid":"E","to":"prslehdaftrcedkyxyfjrnwmybaxmbjbnehh","tpid":"T"},
{"from":"ezhnam","fpid":"B","to":"fzgvx","tpid":"T"},
{"from":"fzgvx","fpid":"B-s-15411","to":"upzkhyiwyouvvenhzmwgxwf","tpid":"T"},
{"from":"fzgvx","fpid":"B-s-15458","to":"hiwhmkscwbengwntw","tpid":"T"},
{"from":"gubfvbudrwumtsstzyics","fpid":"B-s-2012","to":"wglmqhglbmb","tpid":"T"},
{"from":"dqaaohwczlthbhpzkk","fpid":"B","to":"jqxjtuc","tpid":"T"},
{"from":"mjec","fpid":"B-s-19949","to":"xhtttnsdkrmzpod","tpid":"T"},
{"from":"mjec","fpid":"B-s-19949","to":"ooezdivexhsrpguriuki","tpid":"T"},
{"from":"ooezdivexhsrpguriuki","fpid":"B","to":"sfjnanljslirukwmevb","tpid":"T"},
{"from":"sfjnanljslirukwmevb","fpid":"B","to":"jqxjtuc","tpid":"T"},
{"from":"jqxvauyertbvret","fpid":"B","to":"joyqwwzdnrzihdbmpcgzoq","tpid":"T"},
{"from":"ajipqcxmdzlcjrcnvou","fpid":"B-strDM","to":"ulyxsahreacdhwllzwfeb","tpid":"T"},
{"from":"rqcgfmmktvimtahqszfeghjbpaxx","fpid":"B","to":"joyqwwzdnrzihdbmpcgzoq","tpid":"T"},
{"from":"boiczzipkpegrmcocrcxdqzelhyhf","fpid":"B","to":"joyqwwzdnrzihdbmpcgzoq","tpid":"T"},
{"from":"wglmqhglbmb","fpid":"B-strRetailDm","to":"kphjyebilnzdjlnkltjnxamhzual","tpid":"T"},
{"from":"tmsbkgstkhlywga","fpid":"B-getAldDecision","to":"kphjyebilnzdjlnkltjnxamhzual","tpid":"T"},
{"from":"pshcahzogdgeubxiiot","fpid":"B","to":"kphjyebilnzdjlnkltjnxamhzual","tpid":"T"},
{"from":"hiwhmkscwbengwntw","fpid":"B","to":"jqxjtuc","tpid":"T"}
]}
</textarea>
<button onclick="loadJson()">Load JSON</button>
<!-- <svg id="mySvg" ...>...</svg> -->
<script src="https://unpkg.com/gojs"></script>
<script id="code">
// try to create a model from some SVG rendered by Diagram.makeSvg
function loadFromSvg() {
const svg = document.getElementById("mySvg");
const parts = svg.querySelectorAll('g[data-class-name]');
const nda = [];
const lda = [];
let currentGroup = null;
parts.forEach(p => {
const d = {};
const cls = p.getAttribute("data-class-name");
if (p.getAttribute("data-key")) d.key = p.getAttribute("data-key");
if (cls === "Group") {
d.isGroup = true;
currentGroup = d;
}
if (cls === "Link") {
if (p.getAttribute("data-from-key")) d.from = p.getAttribute("data-from-key");
if (p.getAttribute("data-from-port-id")) d.fpid = p.getAttribute("data-from-port-id");
if (p.getAttribute("data-to-key")) d.to = p.getAttribute("data-to-key");
if (p.getAttribute("data-to-port-id")) d.tpid = p.getAttribute("data-to-port-id");
lda.push(d);
} else {
//??? not handling ports: "[data-port-id]"
if (p.getAttribute("data-group")) {
d.group = p.getAttribute("data-group");
} else { //??? guess about membership
if (currentGroup && cls !== "Group") d.group = currentGroup.key;
}
nda.push(d);
}
});
myDiagram.model = new go.GraphLinksModel({
linkFromPortIdProperty: "fpid",
linkToPortIdProperty: "tpid",
nodeDataArray: nda,
linkDataArray: lda
});
}
const myDiagram =
new go.Diagram("myDiagramDiv", {
layout: new go.LayeredDigraphLayout({
isOngoing: false,
isInitial: false,
direction: 90,
setsPortSpots: false
}),
"undoManager.isEnabled": true,
"ModelChanged": e => { // just for demonstration purposes,
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
myDiagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape({ fill: "white" }),
new go.TextBlock({ margin: 8 })
.bind("text", "key")
);
myDiagram.groupTemplate =
new go.Group("Auto", {
avoidable: false,
layout: new go.LayeredDigraphLayout({
//columnSpacing: 40,
//layerSpacing: 80,
//layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource,
//aggressiveOption: go.LayeredDigraphLayout.AggressiveMore,
//packOption: go.LayeredDigraphLayout.PackAll,
isOngoing: false,
isInitial: false,
isRealtime: false,
direction: 90,
setsPortSpots: false
})
})
.add(
new go.Shape({ fill: "whitesmoke" }),
new go.Panel("Vertical", { margin: 5 })
.add(
new go.Panel("Horizontal")
.add(
go.GraphObject.build("SubGraphExpanderButton"),
new go.TextBlock()
.bind("text", "key")
),
new go.Placeholder({ padding: 10 })
)
);
myDiagram.linkTemplate =
new go.Link({ routing: go.Link.AvoidsNodes, corner: 10 })
.add(
new go.Shape({ strokeWidth: 2 }),
new go.Shape({ toArrow: "OpenTriangle" })
);
function loadJson() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
myDiagram.layoutDiagram(true); // force complete layout
}
function collapse() {
myDiagram.commit(diag => {
diag.findTopLevelGroups().each(g => g.isSubGraphExpanded = false);
});
}
function expand() {
myDiagram.commit(diag => {
diag.findTopLevelGroups().each(g => g.isSubGraphExpanded = true);
});
}
loadJson();
</script>
</body>
</html>