//
//     Copyright © 2011-2025 Cambridge Intelligence Limited.
//     All rights reserved.
//
//     Sample Code
//
//!    Reveal underlying links between combos.
import KeyLines from "keylines";
import { chartOptions } from "./reveallinks-data.js";

let chart;
let graphEngine;

function linkedToOpenCombo(linkId) {
  const link = chart.getItem(linkId);
  const node1 = chart.combo().find(link.id1);
  const node2 = chart.combo().find(link.id2);
  // Filter out non-combo ends of the link
  const linkEndCombos = [node1, node2].filter((id) =>
    chart.combo().isCombo(id)
  );
  // Return true only if one of the links is an open combo
  return linkEndCombos.some((comboId) => chart.combo().isOpen(comboId));
}

function selectionHandler() {
  // Get the current state
  const ids = chart
    .selection()
    .filter((id) => chart.getItem(id).type === "node");
  if (ids.length > 0) {
    // Get, show (if required), and foreground the neighbours of the underlying items
    graphEngine.load(chart.serialize());
    // The method for getting neighbours of combos and noncombos is slightly different
    // So we have to use a different method for each
    const combos = ids.filter((id) => chart.combo().isCombo(id));
    const comboNeighbours = chart.graph().neighbours(combos);
    const nonCombos = ids.filter((id) => !chart.combo().isCombo(id));
    const nonComboNeighbours = graphEngine.neighbours(nonCombos);
    // Get all the links involved
    const links = comboNeighbours.links.concat(nonComboNeighbours.links);
    // And filter them such that they only include ones linked to an open combo.
    chart.combo().reveal(links.filter(linkedToOpenCombo));
    if (combos.length) {
      // This allows combos to become selected as there's no 'all' option for chart.foreground
      chart.foreground((item) => links.includes(item.id), {
        type: "link",
        items: "toplevel",
      });
    }
    if (nonCombos.length) {
      chart.foreground((item) => links.includes(item.id), { type: "link" });
    }
  } else {
    // No items selected, foreground everything
    chart.combo().reveal([]);
    chart.foreground(() => true);
  }
}

async function comboHandler(id) {
  const comboId = chart.combo().find(id) || id;
  const opts = { adapt: "inCombo" };

  if (chart.combo().isCombo(comboId)) {
    chart.foreground(() => true);
    chart.combo().reveal([]);
    if (chart.combo().isOpen(comboId)) {
      await chart.combo().close(comboId, opts);
    } else {
      await chart.combo().open(comboId, opts);
    }
  }
}

function doubleClickHandler({ preventDefault, id }) {
  if (chart.getItem(id)) {
    comboHandler(id);
    selectionHandler();
    preventDefault();
  }
}

async function startKeyLines() {
  chart = await KeyLines.create({
    container: "klchart",
    options: {
      drag: {
        links: false,
      },
      selectionColour: "#444",
      logo: { u: "/images/Logo.png" },
    },
  });

  graphEngine = KeyLines.getGraphEngine();

  // data is defined in reveallinks-data.js
  chart.load(chartOptions);

  // Setup UI
  chart.on("selection-change", selectionHandler);
  chart.on("double-click", doubleClickHandler);

  chart.layout("organic");
}

window.addEventListener("DOMContentLoaded", startKeyLines);
<!DOCTYPE html>
<html lang="en" style="background-color: #2d383f;">
  <head>
    <meta charset="UTF-8">
    <title>Combos: Reveal Links</title>
    <link rel="stylesheet" type="text/css" href="/css/keylines.css">
    <link rel="stylesheet" type="text/css" href="/css/minimalsdk.css">
    <link rel="stylesheet" type="text/css" href="/css/sdk-layout.css">
    <link rel="stylesheet" type="text/css" href="/css/demo.css">
    <link rel="stylesheet" type="text/css" href="/css/font-awesome.css">
    <script src="/vendor/jquery.js" defer type="text/javascript"></script>
    <script id="keylines" src="/public/keylines.umd.cjs" defer type="text/javascript"></script>
    <script src="/reveallinks.js" crossorigin="use-credentials" type="module"></script>
  </head>
  <body>
    <div class="chart-wrapper demo-cols">
      <div class="tab-content-panel flex1" id="lhs" data-tab-group="rhs">
        <div class="toggle-content is-visible" id="lhsVisual" style="width:100%; height: 100%;">
          <div class="klchart" id="klchart">
          </div>
        </div>
      </div>
      <div class="rhs citext closed" id="demorhscontent">
        <div class="title-bar">
          <svg viewBox="0 0 360 60" style="max-width: 360px; max-height: 60px; font-size: 24px; font-family: Raleway; flex: 1;">
            <text x="0" y="38" style="width: 100%;">Combos: Reveal Links</text>
          </svg>
        </div>
        <div class="tab-content-panel" data-tab-group="rhs">
          <div class="toggle-content is-visible tabcontent" id="controlsTab">
            <p>Select items in the chart to reveal their direct connections.</p>
            <form autocomplete="off" onsubmit="return false" id="rhsForm">
            </form>
          </div>
        </div>
      </div>
    </div>
    <div id="moreParent">
      <div id="moreContainer">
      </div>
    </div>
    <div id="lazyScripts">
    </div>
  </body>
</html>
Loading source