//
// Copyright © 2011-2025 Cambridge Intelligence Limited.
// All rights reserved.
//
// Sample Code
//
//! Rearrange selected nodes to improve clarity.
import KeyLines from "keylines";
import { data } from "./arrange-data.js";
let chart;
const tightValueSlider = document.getElementById("slider");
// Returns the selected radio button
// name should be : shape, criteria, or position
function getSelectedRadioButton(name) {
return document.querySelector(`input[name=${name}]:checked`).value;
}
async function doArrange(items) {
const idList = items.map((item) => item.id);
const tightness = +tightValueSlider.value;
const position = getSelectedRadioButton("position");
const options = { tightness, position, fit: true };
await chart.arrange(getSelectedRadioButton("shape"), idList, options);
}
function sortByLabel(items) {
return items.sort((a, b) => a.t.localeCompare(b.t));
}
function sortByValues(items, sortValues) {
return items.slice().sort((a, b) => sortValues[b.id] - sortValues[a.id]);
}
function isNode(item) {
return item && item.type === "node";
}
async function startArrange() {
// Filter nodes from the whole selection
const selectedItems = chart.getItem(chart.selection());
const selectedNodes = selectedItems.filter(isNode);
// Change the selection only to nodes
chart.selection(selectedNodes.map((item) => item.id));
// Get the selected criteria from the right panel
const criteria = getSelectedRadioButton("criteria");
// sort items and arrange them
let orderedList;
switch (criteria) {
case "alpha":
orderedList = sortByLabel(selectedNodes);
break;
case "betweenness":
{
const betweennessValues = await chart.graph().betweenness({});
orderedList = sortByValues(selectedNodes, betweennessValues);
}
break;
case "degree":
{
const degreesValues = chart.graph().degrees();
orderedList = sortByValues(selectedNodes, degreesValues);
}
break;
default:
break;
}
doArrange(orderedList);
}
function updateSliderValue() {
const tightValue = tightValueSlider.value;
document.getElementById("tighValueDisplayed").innerHTML = ` ${tightValue}`;
}
function createEventHandlers() {
const arrangeButton = document.getElementById("arrangeButton");
arrangeButton.addEventListener("click", startArrange);
const layoutButton = document.getElementById("layoutButton");
layoutButton.addEventListener("click", () => chart.layout());
tightValueSlider.addEventListener("input", updateSliderValue);
}
async function loadKeyLines() {
const options = {
drag: {
links: false,
},
logo: "/images/Logo.png",
minZoom: 0.01,
};
chart = await KeyLines.create({
id: "kl",
container: "klchart",
options,
});
chart.load(data);
chart.layout();
createEventHandlers();
}
window.addEventListener("DOMContentLoaded", loadKeyLines);
<!DOCTYPE html>
<html lang="en" style="background-color: #2d383f;">
<head>
<meta charset="UTF-8">
<title>Arrange 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">
<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="/arrange.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%;">Arrange Items</text>
</svg>
</div>
<div class="tab-content-panel" data-tab-group="rhs">
<div class="toggle-content is-visible tabcontent" id="controlsTab">
<p>Select a group of nodes in the chart, then click the 'Arrange' button. Play with the settings to see the effect they have on the arrangement.</p>
<form autocomplete="off" onsubmit="return false" id="rhsForm">
<div class="cicontent">
<fieldset style="margin-left:8px; margin-bottom: 5px;">
<legend>Selection shape:</legend>
<label class="radio inline">
<input type="radio" name="shape" value="grid" checked>Grid
</label>
<label class="radio inline">
<input type="radio" name="shape" value="circle">Circle
</label>
<label class="radio inline">
<input type="radio" name="shape" value="radial">Radial
</label>
</fieldset>
<fieldset style="margin-left:8px; margin-bottom: 5px;">
<legend>Tightness: <span id="tighValueDisplayed" style="font-size: 100%;">5</span></legend>
<label class="radio inline">
<input type="range" min="0" max="10" value="5" id="slider">
</label>
</fieldset>
<fieldset style="margin-left:8px; margin-bottom: 5px;">
<legend>Order criteria:</legend>
<label class="radio inline">
<input type="radio" name="criteria" value="alpha" checked>Alphabetic
</label>
<label class="radio inline">
<input type="radio" name="criteria" value="betweenness">Betweenness
</label>
<label class="radio inline">
<input type="radio" name="criteria" value="degree">Degree
</label>
</fieldset>
<fieldset style="margin-left:8px; margin-bottom: 20px;">
<legend>Grid position:</legend>
<label class="radio">
<input type="radio" name="position" value="absolute">Absolute position
</label>
<label class="radio">
<input type="radio" name="position" value="average" checked>Average nodes position
</label>
<label class="radio">
<input type="radio" name="position" value="tidy">Rearrange selected nodes
</label>
</fieldset>
<fieldset style="margin-left:8px; margin-bottom: 5px;">
<div class="form-inline">
<input class="btn btn-spaced" id="arrangeButton" type="button" value="Arrange">
<input class="btn btn-spaced" id="layoutButton" type="button" value="Layout">
</div>
</fieldset>
</div>
</form>
</div>
</div>
</div>
</div>
<div id="moreParent">
<div id="moreContainer">
<div id="applemenumask"></div>
</div>
</div>
<div id="lazyScripts">
</div>
</body>
</html>
Loading source
