//
//     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