//
//     Copyright © 2011-2025 Cambridge Intelligence Limited.
//     All rights reserved.
//
//     Sample Code
//
//!    Use tooltips to display useful information about nodes and links.
import KeyLines from "keylines";
import { data } from "./tooltips-data.js";

// declaring global vars
let chart;

const nodeBaseSize = 26;
const tooltipContainer = document.querySelector("#tooltip-container");
const tooltip = {
  itemId: null,
  element: null,
};

// Create the HTML code that is going to fill the tooltip
const templateHtml = document.getElementById("tt_html").innerHTML;

function updateTooltipPosition() {
  if (tooltip.itemId) {
    const { itemId, element } = tooltip;
    const item = chart.getItem(itemId);
    const coordinates = chart.viewCoordinates(item.x, item.y);
    const x = coordinates.x;
    const y = coordinates.y;
    const zoom = chart.viewOptions().zoom;
    const arrowTipOffset = 20;
    // get the size of the node on screen
    const nodeSize = nodeBaseSize * (item.e || 1) * zoom;
    element.style.opacity = 0;
    // allow fade in and out to animate
    element.style.transition = "opacity 0.3s ease";
    // scale the size of the tooltip depending on zoom level
    element.style.transform = `scale(${Math.max(0.75, Math.min(2, zoom))}`;
    const top = y - element.clientHeight / 2;
    element.style.left = `${x + arrowTipOffset + nodeSize}px`;
    element.style.top = `${top}px`;
    element.style.opacity = 1;
  }
}

function closeTooltip() {
  if (tooltip.element) {
    tooltip.element.style.opacity = 0;
    tooltip.itemId = null;
    tooltip.element = null;
  }
}

function handleTooltip({ id }) {
  const item = chart.getItem(id);
  if (item && item.type === "node") {
    const html = templateHtml
      .replace(/{{label}}/, item.t)
      .replace(/{{gender}}/, item.d.g)
      .replace(/{{name}}/, item.d.fn)
      .replace(/{{surname}}/, item.d.ln);

    // Add it to the DOM
    tooltipContainer.innerHTML = html;
    tooltip.element = document.getElementById("tooltip");
    tooltip.itemId = id;
    updateTooltipPosition();
  } else if (tooltip.element) {
    closeTooltip();
  }
}

function chartLoaded() {
  let { width, height } = chart.viewOptions();
  chart.on("view-change", () => {
    const { width: newWidth, height: newHeight } = chart.viewOptions();
    if (width !== newWidth || height !== newHeight) {
      closeTooltip();
      width = newWidth;
      height = newHeight;
    }
    updateTooltipPosition();
  });
  chart.on("drag-move", updateTooltipPosition);
  chart.on("hover", handleTooltip);

  chart.load(data);
  chart.layout();
}

async function startKeyLines() {
  const options = {
    drag: {
      links: false,
    },
    logo: { u: "/images/Logo.png" },
    hover: 100,
    iconFontFamily: "Font Awesome 5 Free",
    imageAlignment: { ["far fa-user"]: { dy: -5, e: 0.9 } },
  };

  chart = await KeyLines.create({
    container: "klchart",
    options,
  });
  chartLoaded();
}

function loadKeyLines() {
  document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}

window.addEventListener("DOMContentLoaded", loadKeyLines);
<!DOCTYPE html>
<html lang="en" style="background-color: #2d383f;">
  <head>
    <meta charset="UTF-8">
    <title>Tooltips on Items</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/fontawesome5/fontawesome.css">
    <link rel="stylesheet" type="text/css" href="/css/fontawesome5/regular.css">
    <link rel="stylesheet" type="text/css" href="/tooltips.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="/tooltips.js" crossorigin="use-credentials" defer 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 id="tooltip-container"></div>
          </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%;">Tooltips on Items</text>
          </svg>
        </div>
        <div class="tab-content-panel" data-tab-group="rhs">
          <div class="toggle-content is-visible tabcontent" id="controlsTab">
            <form autocomplete="off" onsubmit="return false" id="rhsForm">
              <div class="cicontent">
                <p>Hover over the chart nodes (or tap on touch devices) to show tooltips with details.</p>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
    <div id="moreParent">
      <div id="moreContainer">
        <template id="tt_html">
          <div class="popover right" id="tooltip" style="margin:0px; transform-origin:left;position: absolute; min-width: 70px; pointer-events: none;">
            <div class="arrow"></div>
            <h2 class="popover-title"><strong>{{label}}</strong></h2>
            <div class="popover-content">
              <table class="table-condensed">
                <tbody>
                  <tr>
                    <td style="text-align: right;"><strong>Gender</strong></td>
                    <td>{{gender}}</td>
                  </tr>
                  <tr>
                    <td style="text-align: right;"><strong>Name</strong></td>
                    <td>{{name}}</td>
                  </tr>
                  <tr>
                    <td style="text-align: right;"><strong>Surname</strong></td>
                    <td>{{surname}}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
        </template>
      </div>
    </div>
    <div id="lazyScripts">
    </div>
  </body>
</html>
#htmlModal {
  -webkit-user-select: text;
  -khtml-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
}

/* Tooltip styling */
.popover {
  z-index: 1000;
  background: white;
}
:fullscreen .popover {
  margin-top: -70px;
}
#tooltip .popover-title {
  font-size: 16px;
  padding: 8px 14px;
  margin: 0px;
  line-height: 20px;
  top: 0px;
}
#tooltip .popover-content {
  font-size: 12px;
  border: 1px solid gray;
}
#tooltip .popover-content .td {
  border: 1px solid gray;
  max-height: 20px;
}
#tooltip .arrow {
  background-color: white;
  border-bottom: 1px solid gray;
  border-left: 1px solid gray;
  transform: translate(-5px, 60px) rotateZ(45deg);
  width: 10px;
  height: 10px;
  position: absolute;
}

.popover-content {
  padding: 9px 14px;
}


.klchart {
  overflow: hidden;
}

#tooltip {
  background: white;
  border: black;
  transition: opacity 0.3s ease;
}
Loading source