//
// Copyright © 2011-2025 Cambridge Intelligence Limited.
// All rights reserved.
//
// Sample Code
//
//! Explore ways to style nodes and make them stand out.
import KeyLines from "keylines";
import { data } from "./nodestyles-data.js";
let chart;
// Puts the selected item and its group into foreground, and everything else into the background
function highlightSection(id) {
// if the user hasn't clicked an element resize back to fit
if (!id) {
chart.foreground(() => true);
return;
}
// if there is a clicked item set it to be foreground
const clickedItem = chart.getItem(id);
if (clickedItem) {
chart.foreground((item) => clickedItem.id === item.id);
}
}
function formatPrintString(key, value) {
if (key === "x" || key === "y" || key === "bg") {
return undefined;
}
// prints the borderRadius array as a single value if all directions are set to the same value
if (key === "borderRadius") {
const set = new Set(value);
if (set.size === 1) {
return value[0];
}
}
return value;
}
// Formats the code snippets neatly in the display box on the right-hand side
function prettyPrint(id) {
const item = chart.getItem(id);
if (item) {
const json = JSON.stringify(item, formatPrintString, 1);
document.getElementById("display").innerHTML = json.replace(
/"(t)":\s?"(KeyLines\.getFontIcon[^"]+)"/g,
'"$1": $2'
);
} else {
document.getElementById("display").innerHTML = "No node selected";
}
}
// Starts and stops the glyph animation
let animated = false;
function startStopAnimation() {
animated = !animated;
// get the node to be animated
const animatedNode = chart.getItem("g3");
// update the first glyph of the selected item by toggling the animation option on/off
animatedNode.g[0].a = !animatedNode.g[0].a;
chart.setProperties(animatedNode);
// Ensure that the displayed source code reflects the current state of the animated element.
if (chart.selection()[0] === "g3") {
prettyPrint("g3");
}
}
// Called when KeyLines is created
function initializeInteractions() {
// Calls highlighting and pretty printing when a selection is made
chart.on("selection-change", () => {
const id = chart.selection()[0];
highlightSection(id);
prettyPrint(id);
});
// Zoom the view to its original position when double-clicking the background
chart.on("double-click", ({ id, preventDefault }) => {
if (!chart.getItem(id)) {
chart.zoom("fit", { animate: true, time: 500 });
}
// Default behaviour is to zoom in - we preventDefault to override this
preventDefault();
});
// prevent nodes from being dragged
chart.on("drag-start", ({ preventDefault, type }) => {
if (type === "node") {
preventDefault();
}
});
// Listen to the checkbox change event so that the startStopAnimation function
// updates the animation property of the glyph.
document
.getElementById("animateGlyph")
.addEventListener("change", startStopAnimation);
}
// Custom image alignments for fontawesome nodes
const imageAlignment = {
"fas fa-user": { dy: -8 },
"far fa-user": { dy: -8 },
"fas fa-exclamation-triangle": { dy: -10 },
};
async function startKeyLines() {
const options = {
logo: { u: "/images/Logo.png" },
handMode: true,
selectedNode: { b: "#FF9933" },
iconFontFamily: "Font Awesome 5 Free",
imageAlignment,
overview: { icon: false },
backColour: "rgb(238,238,238)",
};
chart = await KeyLines.create({ container: "klchart", options });
chart.load(data);
chart.zoom("fit");
initializeInteractions();
}
function loadFontsAndStart() {
document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}
window.addEventListener("DOMContentLoaded", loadFontsAndStart);
<!DOCTYPE html>
<html lang="en" style="background-color: #2d383f;">
<head>
<meta charset="UTF-8">
<title>Node Styles</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/solid.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="/nodestyles.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>
</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%;">Node Styles</text>
</svg>
</div>
<div class="tab-content-panel" data-tab-group="rhs">
<div class="toggle-content is-visible tabcontent" id="controlsTab">
<p>Select a node to view its source code.</p>
<form autocomplete="off" onsubmit="return false" id="rhsForm">
<div class="cicontent">
<fieldset>
<pre id="display" style="-webkit-user-select: text; -khtml-user-select: text; -moz-user-select: text; -ms-user-select: text; word-break: keep-all;">No node selected</pre>
<label class="checkbox" style="margin-top:20px;">
<input id="animateGlyph" type="checkbox"><span> Show Animated Glyph</span>
</label>
</fieldset>
</div>
</form>
</div>
</div>
</div>
</div>
<div id="moreParent">
<div id="moreContainer">
</div>
</div>
<div id="lazyScripts">
</div>
</body>
</html>
Loading source
