//
// Copyright © 2011-2025 Cambridge Intelligence Limited.
// All rights reserved.
//
// Sample Code
//
//! Explore ways to style links and make them stand out.
import KeyLines from "keylines";
import { data } from "./linkstyles-data.js";
let chart;
let doAnts = false;
// Alter the view by zooming to the 'fit' position
function fitZoom(animate) {
chart.zoom("fit", { animate, time: 500 });
}
// Format the properties of the displayed item (may accept a nav control id)
function prettyPrint() {
const id = chart.selection()[0];
const item = chart.getItem(id);
if (item) {
// Avoid printing the coordinates of the elements in the display window
delete item.x;
delete item.y;
document.getElementById("display").innerHTML = JSON.stringify(
item,
null,
1
);
} else if (!id) {
// If the background was clicked, clear the selection
document.getElementById("display").innerHTML = "No item selected";
}
}
function toggleAnts() {
doAnts = !doAnts;
// Update the flow property of the nodes having an id
// that starts with 'flow'.
chart.each({ type: "link" }, (link) => {
if (link.d && link.d.flow) {
chart.setProperties({
id: link.id,
flow: doAnts ? { velocity: link.d.flow } : undefined,
});
}
});
// Update the source code displayed to reflect the flow changes.
prettyPrint();
}
function initializeInteractions() {
// When clicking on an item, show its properties on the right-hand side
chart.on("selection-change", prettyPrint);
// Zoom the view to its original position when double-clicking the background
chart.on("double-click", ({ id, preventDefault }) => {
if (!chart.getItem(id)) {
fitZoom(true);
}
// Default behaviour is to zoom in - we preventDefault to override this
preventDefault();
});
// prevent nodes from being dragged
chart.on("drag-start", ({ setDragOptions, type }) => {
if (type === "node") {
setDragOptions({ type: chart.options().handMode ? "pan" : "marquee" });
}
});
const inlineElement = document.getElementById("inlineEnabled");
document.getElementById("flowEnabled").addEventListener("change", toggleAnts);
inlineElement.addEventListener("change", () => {
chart.options({
linkStyle: {
inline: !!inlineElement.checked,
},
});
});
}
async function loadKeylines() {
const options = {
logo: { u: "/images/Logo.png" },
selectionColour: "#FF9933",
selfLinks: true,
overview: { icon: false },
handMode: true,
};
chart = await KeyLines.create({ container: "klchart", options });
chart.load(data); // load the chart data
fitZoom(); // adjust the view to 'fit' the displayed chart
initializeInteractions(); // Attach the chart and user events
}
window.addEventListener("DOMContentLoaded", loadKeylines);
<!DOCTYPE html>
<html lang="en" style="background-color: #2d383f;">
<head>
<meta charset="UTF-8">
<title>Style 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">
<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="/linkstyles.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%;">Style Links</text>
</svg>
</div>
<div class="tab-content-panel" data-tab-group="rhs">
<div class="toggle-content is-visible tabcontent" id="controlsTab">
<p>Select an item to view its source.</p>
<form autocomplete="off" onsubmit="return false" id="rhsForm">
<div class="cicontent">
<div class="settings">
<label class="checkbox" style="margin-bottom:16px;margin-top:16px;">
<input id="flowEnabled" type="checkbox"><span> Enable Flow</span>
</label>
<label class="checkbox" style="margin-bottom:16px;">
<input id="inlineEnabled" type="checkbox"><span> Inline Labels </span>
</label>
<pre id="display" style="-webkit-user-select: text; -khtml-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; word-break: keep-all;">No item selected</pre>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div id="moreParent">
<div id="moreContainer">
</div>
</div>
<div id="lazyScripts">
</div>
</body>
</html>
Loading source
