Skip to content
  1. Examples

Hierarchical advanced

This example shows how to choose which node to place on top of the hierarchy. Click on a node to promote it at the top of the tree.
Click an edge to make its extremities the top of the layout.

ts
import Ogma, { HierarchicalLayoutOptions, Options } from '@linkurious/ogma';
import { GUI } from 'dat.gui';
import './styles.css';

const ogma = new Ogma({
  container: 'graph-container',
  renderer: 'canvas',
  options: {
    edgesRoutingStyle: 'vertical'
  }
});

ogma.styles.addRule({
  nodeAttributes: {
    radius: 2,
    color: '#0094ff'
  },
  edgeAttributes: {
    shape: {
      head: 'arrow'
    }
  }
});

const defaultLayoutOptions: HierarchicalLayoutOptions = {
  direction: 'TB', // Direction of the layout. Can be TB, BT, LR, or RL,
  // where T = top, B = bottom, L = left, and R = right.
  duration: 300, // Duration of the animation
  nodeDistance: 20, // Number of pixels that separate nodes horizontally in the layout.
  levelDistance: 50, // Number of pixels between each layer in the layout.
  componentDistance: 16, // Number of pixels between each component in the layout.
  arrangeComponents: 'fit',
  locate: {
    easing: 'linear',
    duration: 300
  }
};

const ogmaOptions: Options = {
  edgesRoutingStyle: 'vertical',
  renderer: 'canvas'
};

const runLayout = (options: HierarchicalLayoutOptions) =>
  ogma.layouts.hierarchical(options);

function runWithParameters() {
  // create fresh new options
  const newOptions: HierarchicalLayoutOptions = { ...defaultLayoutOptions };
  console.log(defaultLayoutOptions);
  // pass the roots to the layout
  newOptions.roots = ogma.getSelectedNodes();
  runLayout(newOptions);
}

const gui = new GUI({ name: 'Layout parameters', width: 330 });

// add general group
gui
  .add(defaultLayoutOptions, 'levelDistance', 0, 100)
  .name('Level distance')
  .onChange(runWithParameters);
gui
  .add(defaultLayoutOptions, 'nodeDistance', 0, 100)
  .name('Node distance')
  .onChange(runWithParameters);

gui
  .add(defaultLayoutOptions, 'componentDistance', 0, 100)
  .name('Component distance')
  .onChange(runWithParameters);
gui
  .add(defaultLayoutOptions, 'direction', {
    'Top Down': 'TB',
    'Bottom Up': 'BT',
    'Left to Right': 'LR',
    'Right to Left': 'RL'
  })
  .name('Direction')
  .onChange(runWithParameters);

gui
  .add(defaultLayoutOptions, 'arrangeComponents', {
    Fit: 'fit',
    Grid: 'grid',
    Line: 'singleLine'
  })
  .name('Arrange components')
  .onChange(runWithParameters);

gui
  .add(ogmaOptions, 'edgesRoutingStyle', ['none', 'horizontal', 'vertical'])
  .name('Edges routing')
  .onChange(() => {
    ogma.setOptions(ogmaOptions);
  });

gui
  .add(ogmaOptions, 'renderer', ['canvas', 'webgl', 'svg'])
  .name('Renderer')
  .onChange(() => {
    ogma.setOptions(ogmaOptions);
  });

const graph = await ogma.generate.balancedTree({
  children: 2,
  height: 5
});
// add some extra nodes to show multi-component packing
const extraNodes = Array.from({ length: 25 }).map((_, i) => {
  return { id: 1000 + i };
});
graph.nodes = graph.nodes.concat(extraNodes);

await ogma.setGraph(graph);
ogma.getNode(0)!.setSelected(true);
runWithParameters();

ogma.events.on('click', evt => {
  const target = evt.target;

  // If the user clicked a node set it as root,
  // otherwise pick the link ends and make them roots
  if (target) {
    if (target.isNode) {
      target.setSelected(true);
    } else {
      target.getExtremities().setSelected(true);
    }
  }

  runWithParameters();
});
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link
      href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
      rel="stylesheet"
    />
  </head>

  <body>
    <div id="graph-container"></div>
    <script type="module" src="index.ts"></script>
  </body>
</html>
css
:root {
  --base-font-family: 'IBM Plex Sans', Arial, Helvetica, sans-serif;
  font-family: var(--base-font-family);
}

#graph-container {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: 0;
  overflow: hidden;
}