Five clusters. Group collapses each cluster to a meta-node with aggregated edges; Ungroup restores byte-for-byte. Double-click a meta-node to expand just that one.
// Nodes carry only a `data.cluster` key; the application does not calculate meta-node
// positions or aggregate edges. OGJS groups and restores the graph transactionally while
// preserving stable ids, labels, camera context, and the ability to expand one group.
import { Ogjs } from '../../src/index';
const og = new Ogjs({ container: '#viz', theme: 'light' });
const nodes = [], edges = [];
const PAL = ['#4f8cff', '#47d78a', '#f2a33c', '#e5484d', '#9d6bff'];
// Group membership is ordinary application data. These edges remain the source topology;
// OGJS aggregates them while collapsed and restores them when members return.
for (let c = 0; c < 5; c++) for (let k = 0; k < 24; k++) {
const i = c * 24 + k;
nodes.push({ id: i, size: k === 0 ? 10 : 4, color: PAL[c],
label: k === 0 ? 'cluster ' + c : undefined, data: { cluster: c } });
if (k > 0) edges.push({ source: i, target: c * 24 + (k > 4 ? (i % k) : 0) });
}
for (let c = 0; c < 5; c++) edges.push({ source: c * 24, target: ((c + 1) % 5) * 24 });
og.setGraph({ nodes, edges });
og.layouts.force({ sync: true, iterations: 260, springLength: 46, repulsion: 1400 });
og.camera.fit();
og.enableHoverHighlight();
document.querySelector('#group').addEventListener('click', async () => {
// The key function is the complete grouping instruction. OGJS creates meta-nodes, places
// them, animates the transition, and retains the member graph for reversal.
await og.groupByAnimated(({ data }) => 'cluster ' + data.cluster);
await og.animateFit();
});
document.querySelector('#ungroup').addEventListener('click', async () => {
// Ungroup restores the retained topology; the application does not keep a second graph
// snapshot or manually reconnect aggregate relationships.
await og.ungroupAnimated();
await og.animateFit();
});
og.on('nodedblclick', async (e) => {
const id = og.graph.getNodeId(e.nodeIndex);
if (typeof id === 'string' && id.startsWith('__group:')) {
// Expand only the activated meta-node. Other clusters and the surrounding camera context
// remain intact while OGJS places the revealed members.
await og.expandGroup(id.slice(8));
}
});