Skip to content
  1. Examples

Fast highlight

This example shows how to highlight nodes and edges in the graph. It uses the Classes and the mousemove event to highlight the nodes and edges when the mouse hovers over them.

ts
import Ogma, { Color, NodeId } from '@linkurious/ogma';

const ogma = new Ogma({
  container: 'graph-container'
});

const names: Record<NonNullable<Color>, string> = {
  'rgba(51, 153, 255, 1)': 'Arctic, communities',
  'rgba(0, 204, 204, 1)': 'Research',
  'rgba(153, 255, 255, 1)': 'Population',
  'rgba(255, 204, 51, 1)': 'Aerospace',
  'rgba(255, 204, 102, 1)': 'Geosciences',
  'rgba(255, 255, 51, 1)': 'Physics',
  'rgba(102, 0, 102, 1)': 'Petrol',
  'rgba(153, 0, 0, 1)': 'Political Studies',
  'rgba(102, 102, 0, 1)': 'Pollution',
  'rgba(0, 153, 0, 1)': 'Energy resources',
  'rgba(153, 255, 0, 1)': 'Biodiversity',
  'rgba(0, 204, 51, 1)': 'Global warming'
};

const byColor: Record<NonNullable<Color>, NodeId[]> = {};
Ogma.parse
  .gexfFromUrl('files/arctic.gexf')
  .then(graph => ogma.setGraph(graph))
  .then(() => ogma.view.locateGraph())
  .then(() => {
    const legend = document.createElement('div');
    legend.setAttribute('id', 'legend');
    const legendHtml = [
      '<ul>',
      Object.keys(names)
        .map(color => {
          const name = names[color];
          return (
            '<li><div class="color" style="background-color: ' +
            color +
            '"></div>' +
            name +
            '</li>'
          );
        })
        .join('\n'),
      '</ul>'
    ].join('\n');

    legend.innerHTML = legendHtml;
    document.body.appendChild(legend);

    ogma
      .getNodes()
      .getAttribute('color')
      .reduce((acc, color) => {
        acc[color as NonNullable<Color>] = [];
        return acc;
      }, byColor);
    ogma.getNodes().forEach(n => {
      byColor[n.getAttribute('color') as NonNullable<Color>].push(n.getId());
    });
    Object.keys(byColor).forEach(color => {
      ogma.getNodes(byColor[color]).fillData('groupId', color);
    });
  })
  .then(() => {
    let html = '<ul class="node-list">';
    let prevGroup = '';
    const nodes = ogma.getNodes();
    nodes.forEach(node => node.setData('id', node.getId()));
    nodes
      .getData()
      .sort((a, b) => {
        if (a.groupId > b.groupId) return 1;
        if (a.groupId < b.groupId) return -1;
        return 0;
      })
      .forEach(({ text, groupId, id }) => {
        const dot = `<span class="dot" style="background-color: ${groupId}"></span>`;
        if (prevGroup !== groupId) {
          html += `<li class="header" data-group-id="${groupId}">${dot} ${
            names[groupId] || 'unknown'
          }</li>`;
          prevGroup = groupId;
        }
        html += `<li data-id="${id}">${dot} ${text}</li>`;
      });
    html += '</ul>';

    document.getElementById('content')!.innerHTML = html;
  });

ogma.styles.createClass({
  name: 'highlight',
  nodeAttributes: {
    radius: node => +node.getAttribute('radius') * 3,
    text: { minVisibleSize: 0 },
    innerStroke: { minVisibleSize: 0 },
    layer: 1
  },
  edgeAttributes: {
    layer: 1
  }
});

ogma.styles.createClass({
  name: 'fade',
  nodeAttributes: {
    opacity: 0.2,
    color: 'grey',
    layer: 0
  },
  edgeAttributes: {
    opacity: 0.2,
    color: 'grey',
    layer: 0
  }
});

function highlight(ids: NodeId[]) {
  //if (ids.length === 0) {
  const nodes = ogma.getNodes();
  nodes.removeClass('highlight');
  nodes.removeClass('fade');

  const edges = ogma.getEdges();
  edges.removeClass('highlight');
  edges.removeClass('fade');

  if (ids.length === 0) return;

  const highlighted = ogma.getNodes(ids);
  highlighted.addClass('highlight', 500);
  highlighted.inverse().addClass('fade');

  if (ids.length > 1) {
    const edges = highlighted.getAdjacentEdges({ bothExtremities: true });
    edges.removeClass('fade');
    edges.addClass('highlight', 300);
    edges.inverse().addClass('fade');
  } else {
    ogma.getEdges().addClass('fade');
  }
}

let hovered: string | number | NodeId[] = 0;
const content = document.getElementById('content')!;
content.addEventListener('mousemove', evt => {
  requestAnimationFrame(() => {
    const target = evt.target! as HTMLElement;
    const id = target.getAttribute('data-id');
    if (id && id !== hovered) {
      hovered = id;
      highlight([hovered]);
    } else if (target.hasAttribute('data-group-id')) {
      hovered =
        byColor[target.getAttribute('data-group-id') as NonNullable<Color>];
      highlight(hovered);
    }
  });
});

content.addEventListener('mouseleave', () => {
  requestAnimationFrame(() => {
    hovered = 0;
    highlight([]);
  });
});
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />

    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div class="container">
      <div class="item" id="vis">
        <div id="graph-container"></div>
      </div>
      <div id="pane" class="item">
        <div class="inner" id="content"></div>
      </div>
    </div>
    <script type="module" src="index.ts"></script>
  </body>
</html>
css
body,
html {
  font-family: Helvetica, Arial, sans-serif;
  margin: 0;
  padding: 0;
}

.container {
  display: flex;
  height: 100vh;
  flex-direction: row;
}

.item {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}

#graph-container {
  min-width: 100%;
  height: 100%;
  display: flex;
}

#vis {
  flex-grow: 7;
}

#pane {
  flex-grow: 3;
}

#content {
  width: 100%;
  height: 100%;
  overflow-y: auto;
}

#content ul {
  list-style: none;
  padding: 0;
}

#content ul li {
  padding: 0.5em;
  padding-left: 2em;
  border-bottom: 1px solid #ccc;
  font-size: 0.8em;
  cursor: pointer;
}

#content ul li .dot {
  display: inline-block;
  width: 0.5em;
  height: 0.5em;
  border-radius: 0.5em;
  margin-right: 1em;
}

#content ul li:hover {
  background: #eee;
  font-weight: 700;
}

#content ul .header {
  font-weight: 700;
  padding-left: 1em;
}

#content ul .header .dot {
  border-radius: 0;
}

#legend {
  display: block;
  position: absolute;
  bottom: 20px;
  left: 20px;
}

#legend ul {
  list-style: none;
}

#legend ul li .color {
  display: inline-block;
  width: 1em;
  height: 1em;
  margin-right: 5px;
}

#content ul .header {
  font-weight: 700;
}