Search

Layouts
Scroll to example

Layouts give spatial positions to nodes to help with pattern recognition and other user interactions.

Organic

The default layout, highly optimised for great performance on large datasets. The organic layout excels at untangling big networks and intuitively adapts to updates in the network with as little changes to the overall graph as possible.

Sequential

Arranges nodes in a way that minimises link crossings and makes the most of the available screen space. Useful when nodes are grouped in distinct levels, with a clear sequence of links from one level to the next. Learn more in the Sequential Layouts demo.

Radial

Gives an 'egocentric' view of the network, best thought of as a circular tree-like structure. Nodes are placed in concentric rings based on how well connected they are to a specified node, with better connected items closer to the centre. This layout is useful for seeing the relationships of individual nodes.

Structural

Groups nodes that are linked to the same set of nodes together. Useful with data with a complex structure.

Key function used:

import KeyLines from "keylines";
import { data } from "./data";

let chart;
const orientationSelect = document.getElementById("orientation");
const tightnessSlider = document.getElementById("slider");

// Set nodes 2 & 5 to be the top of sequential layout and centre of radial layout
const top = ["2", "5"];

// Gets the selected layout name
function getLayoutName() {
  return document.querySelector('input[name="layoutName"]:checked').value;
}

function layout() {
  // Use the currently selected layout name and options for the layout
  const layoutName = getLayoutName();
  const orientation =
    orientationSelect.options[orientationSelect.selectedIndex].value;
  const tightness = parseFloat(tightnessSlider.value);

  let options;
  switch (layoutName) {
    case "radial":
      options = { top, tightness };
      break;
    case "sequential":
      options = { top, orientation, tightness };
      break;
    default:
      options = { tightness };
      break;
  }
  chart.layout(layoutName, options);
}

function initialiseInteractions() {
  // Run a layout based on current selected menu options
  document.getElementById("layout").addEventListener("click", layout);

  // Run applicable layout when orientation selection changes
  orientationSelect.addEventListener("change", () => {
    if (getLayoutName() === "sequential") {
      layout();
    }
  });

  // Run a layout when tightness selection changes and update the menu display
  tightnessSlider.addEventListener("change", () => {
    document.getElementById("tightnessValue").innerHTML =
      tightnessSlider.value.toString();
    layout();
  });
}

async function loadKeyLines() {
  const options = {
    drag: {
      links: false,
    },
    logo: { u: "/public/images/Logo.png" },
    handMode: true,
    selectedNode: {
      b: "#082C1A",
      bw: 5,
    },
  };
  chart = await KeyLines.create({ container: "klchart", options });
  chart.load(data);
  chart.layout();
  initialiseInteractions();
}

window.addEventListener("DOMContentLoaded", loadKeyLines);
const nodeBorder = { b: "#19854D", bw: 2 };

const nodeStyles = [
  { c: "#105934", ...nodeBorder },
  { c: "#29de81", ...nodeBorder },
  { c: "#caf7e0", ...nodeBorder },
];

const linkStyle = { c: "#19854D", w: 2, off: 0 };

export const data = {
  type: "LinkChart",
  items: [
    {
      type: "link",
      id: "3-5",
      id1: "3",
      id2: "5",
      ...linkStyle,
    },
    {
      type: "link",
      id: "3-7",
      id1: "3",
      id2: "7",
      ...linkStyle,
    },
    {
      type: "link",
      id: "3-9",
      id1: "3",
      id2: "9",
      ...linkStyle,
    },
    {
      type: "link",
      id: "3-11",
      id1: "3",
      id2: "11",
      ...linkStyle,
    },
    {
      type: "link",
      id: "5-15",
      id1: "5",
      id2: "15",
      ...linkStyle,
    },
    {
      type: "link",
      id: "5-23",
      id1: "5",
      id2: "23",
      ...linkStyle,
    },
    {
      type: "link",
      id: "5-29",
      id1: "5",
      id2: "29",
      ...linkStyle,
    },
    {
      type: "link",
      id: "15-13",
      id1: "15",
      id2: "13",
      ...linkStyle,
    },
    {
      type: "link",
      id: "15-17",
      id1: "15",
      id2: "17",
      ...linkStyle,
    },
    {
      type: "link",
      id: "15-19",
      id1: "15",
      id2: "19",
      ...linkStyle,
    },
    {
      type: "link",
      id: "15-21",
      id1: "15",
      id2: "21",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-35",
      id1: "29",
      id2: "35",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-25",
      id1: "29",
      id2: "25",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-27",
      id1: "29",
      id2: "27",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-31",
      id1: "29",
      id2: "31",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-33",
      id1: "29",
      id2: "33",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-37",
      id1: "29",
      id2: "37",
      ...linkStyle,
    },
    {
      type: "link",
      id: "29-39",
      id1: "29",
      id2: "39",
      ...linkStyle,
    },
    {
      type: "link",
      id: "2-4",
      id1: "2",
      id2: "4",
      ...linkStyle,
    },
    {
      type: "link",
      id: "2-6",
      id1: "2",
      id2: "6",
      ...linkStyle,
    },
    {
      type: "link",
      id: "2-8",
      id1: "2",
      id2: "8",
      ...linkStyle,
    },
    {
      type: "link",
      id: "2-10",
      id1: "2",
      id2: "10",
      ...linkStyle,
    },
    {
      type: "link",
      id: "2-12",
      id1: "2",
      id2: "12",
      ...linkStyle,
    },
    {
      type: "link",
      id: "2-14",
      id1: "2",
      id2: "14",
      ...linkStyle,
    },
    {
      type: "link",
      id: "4-16",
      id1: "4",
      id2: "16",
      ...linkStyle,
    },
    {
      type: "link",
      id: "4-18",
      id1: "4",
      id2: "18",
      ...linkStyle,
    },
    {
      type: "link",
      id: "4-20",
      id1: "4",
      id2: "20",
      ...linkStyle,
    },
    {
      type: "link",
      id: "4-22",
      id1: "4",
      id2: "22",
      ...linkStyle,
    },
    {
      type: "link",
      id: "6-26",
      id1: "6",
      id2: "26",
      ...linkStyle,
    },
    {
      type: "link",
      id: "6-28",
      id1: "6",
      id2: "28",
      ...linkStyle,
    },
    {
      type: "node",
      id: "2",
      ...nodeStyles[0],
    },
    {
      type: "node",
      id: "3",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "4",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "5",
      ...nodeStyles[0],
    },
    {
      type: "node",
      id: "6",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "7",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "8",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "9",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "10",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "11",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "12",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "13",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "14",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "15",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "16",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "17",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "18",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "19",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "20",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "21",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "22",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "23",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "25",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "26",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "27",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "28",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "29",
      ...nodeStyles[1],
    },
    {
      type: "node",
      id: "31",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "33",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "35",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "37",
      ...nodeStyles[2],
    },
    {
      type: "node",
      id: "39",
      ...nodeStyles[2],
    },
  ],
};
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Layouts</title>
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css">
    <link
      rel="stylesheet"
      type="text/css"
      href="@ci/theme/kl/css/minimalsdk.css"
    >
    <link
      rel="stylesheet"
      type="text/css"
      href="@ci/theme/kl/css/sdk-layout.css"
    >
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css">
    <link
      rel="stylesheet"
      type="text/css"
      href="@ci/theme/kl/css/examples-chrome.css"
    >
    <link rel="stylesheet" href="./style.css">
  </head>
  <body>
    <div class="chart-wrapper demo-cols">
      <div id="lhs" class="flex1">
        <div
          id="lhsVisual"
          class="toggle-content is-visible"
          style="width: 100%; height: 100%"
        >
          <div id="klchart" class="klchart"></div>
        </div>
      </div>
      <div class="rhs citext" id="demorhscontent">
        <div class="tab-content-panel">
          <div id="controlsTab" class="toggle-content is-visible tabcontent">
            <p>
              Try out different layouts by selecting one from the list below.
            </p>
            <form id="rhsForm" autocomplete="off" onsubmit="return false;">
              <div class="cicontent">
                <legend>Layout Name</legend>
                <fieldset id="layoutfieldset">
                  <label class="radio"
                    ><input
                      type="radio"
                      name="layoutName"
                      value="organic"
                      checked="checked"
                    >
                    Organic</label
                  ><label class="radio"
                    ><input type="radio" name="layoutName" value="sequential">
                    Sequential</label
                  ><label class="radio"
                    ><input type="radio" name="layoutName" value="radial">
                    Radial</label
                  ><label class="radio"
                    ><input type="radio" name="layoutName" value="structural">
                    Structural</label
                  >
                </fieldset>
                <fieldset>
                  <input
                    class="btn"
                    type="button"
                    value="Run layout"
                    id="layout"
                  >
                </fieldset>
                <fieldset><legend>Options</legend></fieldset>
                <fieldset>
                  <label
                    >Tightness: <strong id="tightnessValue">5</strong>
                    <input
                      id="slider"
                      type="range"
                      min="0"
                      max="10"
                      value="5"
                    ></label
                  >
                </fieldset>
                <fieldset>
                  <label>Orientation (Sequential)</label
                  ><select class="input-block-level" id="orientation">
                    <option value="down">Down</option>
                    <option value="right">Right</option>
                    <option value="up">Up</option>
                    <option value="left">Left</option>
                  </select>
                </fieldset>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
    <script type="module" src="./code.js"></script>
  </body>
</html>
/* --- layouts.css --- */
input[type="range"] {
  width: 100%;
  margin-bottom: 10px;
}
.label-alpha,
.label-beta {
  margin-left: 12px;
}
#layoutfieldset {
  margin-left: 10px;
}
fieldset {
  width: 90%;
  margin-top: 5px;
}
fieldset .radio {
  display: block;
  padding-left: 0px;
}

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.