Search

Loading Large Datasets

Handling Data

Load data dynamically as the user explores the timeline.

Loading Large Datasets
View live example →

Pan and zoom the timeline to see how many events are loaded from the full dataset. Use the drop-down menu to simulate slow server response times.

With very large datasets it can be useful to limit how far a user can zoom out, and load the data in stages as the user pans. This can keep the amount of data loaded into the browser at a manageable level for optimal performance.

See also:

Pan and zoom the timeline to see how many events are loaded from the full dataset. Use the drop-down menu to simulate slow server response times.

With very large datasets it can be useful to limit how far a user can zoom out, and load the data in stages as the user pans. This can keep the amount of data loaded into the browser at a manageable level for optimal performance.

See also:

import { createTimeline } from "kronograph";
import data from "./data";

const eventDisplayHeading = document.getElementById("event-info");
const delaySelection = document.getElementById("delay-select");
const timelineContainer = document.getElementById("my-timeline");

const timeline = createTimeline({ container: timelineContainer });

const oneWeek = 7 * 24 * 60 * 60 * 1000; // a week in milliseconds
const weeksToShow = 2;
const initialRange = oneWeek * weeksToShow;

const timelineOptions = {
  events: {
    heatmapThreshold: 200,
  },
  scales: {
    panLimit: "off",
    zoomOutLimit: {
      unit: "week",
      value: weeksToShow,
    },
  },
};

const {
  metadata,
  entityTypes,
  eventTypes,
  entities,
  events: eventsArray,
} = data;

const firstEventTime = metadata.firstEventTime;
const lastEventTime = metadata.lastEventTime;
const dataTimeWidth = lastEventTime - firstEventTime;
const weeksOfDataToLoad = 4;

let startTime = firstEventTime;
let endTime = firstEventTime + initialRange;

// convert to UTC milliseconds
// see https://kronograph.io/docs#milliseconds for details
function dateToMilliseconds(date) {
  return Date.UTC(
    date.getFullYear(),
    date.getMonth(),
    date.getDate(),
    date.getHours(),
    date.getMinutes(),
    date.getSeconds(),
    date.getMilliseconds(),
  );
}

function setDisplayInfo(shownEventsCount) {
  eventDisplayHeading.innerText = `${shownEventsCount.toLocaleString()} Events currently loaded`;
}

function setLoading(loading) {
  if (loading) {
    timelineContainer.classList.add("story__loading__cursor");
  } else {
    timelineContainer.classList.remove("story__loading__cursor");
  }
}

function updateTimelineData() {
  const buffer = Math.floor(endTime - startTime) / 2;

  setLoading(true);
  // fetch events within the given time range
  const { events, eventCount } = getEvents(
    Math.max(startTime - buffer, firstEventTime),
    endTime + buffer,
  );

  setDisplayInfo(eventCount);

  const setData = () => {
    timeline.set({
      events,
      entities,
      eventTypes,
      entityTypes,
    });
    setLoading(false);
  };

  if (delaySelection.value > 0) {
    setTimeout(setData, delaySelection.value);
  } else {
    setData();
  }
}

function onTimelineRange({ start, end, why }) {
  if (why === "user") {
    startTime = dateToMilliseconds(start);
    endTime = dateToMilliseconds(end);
    updateTimelineData();
  }
}

function initialiseHandlers() {
  timeline.on("range", onTimelineRange);
}

async function startStory() {
  timeline.options(timelineOptions);
  updateTimelineData();
  timeline.fit();
  initialiseHandlers();
}

startStory();

/// /////////////////////////////////////////////////////////////////////////
// the code below is used to simulate fetching event data from a database //
/// /////////////////////////////////////////////////////////////////////////

function toIndex(items) {
  return items.reduce((result, item) => {
    const { id, ...itemWithoutId } = item;
    result[id] = itemWithoutId;
    return result;
  }, {});
}

function offsetEvents(eventArray, offset) {
  return eventArray.map((event) => {
    return {
      ...event,
      id: `${event.id}-${offset}`,
      time: event.time + dataTimeWidth * offset,
    };
  });
}

// simulate fetching data incrementally from a database
function getEvents(requestedStartTime, requestedEndTime) {
  const startTimeOffset = requestedStartTime - firstEventTime;
  const offsetFactor = Math.floor(startTimeOffset / dataTimeWidth);

  let newEvents = [];
  // a full weeks worth of data is loaded in and then repeated for enough weeks to fill the view range when fully zoomed out
  for (let i = 0; i < weeksOfDataToLoad; i++) {
    newEvents = newEvents.concat(offsetEvents(eventsArray, offsetFactor + i));
  }

  const firstEventIndex = newEvents.findIndex(
    (event) => event.time >= requestedStartTime,
  );

  const lastEventIndex = newEvents.findIndex(
    (event) => event.time >= requestedEndTime,
  );

  newEvents = newEvents.slice(firstEventIndex, lastEventIndex);

  return { events: toIndex(newEvents), eventCount: newEvents.length };
}
export default {
  metadata: {
    firstEventTime: 1578197001722.3477,
    lastEventTime: 1578852737532.8713,
  },
  entityTypes: {
    laptop: {
      typeLabel: "Client Devices",
    },
    server: {
      typeLabel: "Cloud Infrastructure",
    },
  },
  eventTypes: {
    logOn: {
      color: "#27ab83",
    },
    logOff: {
      color: "#f58a3d",
    },
    updateInstalled: {
      color: "#f7d06e",
    },
  },
  entities: {
    "laptop-0": {
      type: "laptop",
      label: "Laptop 0",
    },
    "laptop-1": {
      type: "laptop",
      label: "Laptop 1",
    },
    "laptop-2": {
      type: "laptop",
      label: "Laptop 2",
    },
    "laptop-3": {
      type: "laptop",
      label: "Laptop 3",
    },
    "laptop-4": {
      type: "laptop",
      label: "Laptop 4",
    },
    "laptop-5": {
      type: "laptop",
      label: "Laptop 5",
    },
    "laptop-6": {
      type: "laptop",
      label: "Laptop 6",
    },
    "laptop-7": {
      type: "laptop",
      label: "Laptop 7",
    },
    "laptop-8": {
      type: "laptop",
      label: "Laptop 8",
    },
    "laptop-9": {
      type: "laptop",
      label: "Laptop 9",
    },
    "laptop-10": {
      type: "laptop",
      label: "Laptop 10",
    },
    "laptop-11": {
      type: "laptop",
      label: "Laptop 11",
    },
    "laptop-12": {
      type: "laptop",
      label: "Laptop 12",
    },
    "laptop-13": {
      type: "laptop",
      label: "Laptop 13",
    },
    "laptop-14": {
      type: "laptop",
      label: "Laptop 14",
    },
    "laptop-15": {
      type: "laptop",
      label: "Laptop 15",
    },
    "laptop-16": {
      type: "laptop",
      label: "Laptop 16",
    },
    "laptop-17": {
      type: "laptop",
      label: "Laptop 17",
    },
    "laptop-18": {
      type: "laptop",
      label: "Laptop 18",
    },
    "laptop-19": {
      type: "laptop",
      label: "Laptop 19",
    },
    "laptop-20": {
      type: "laptop",
      label: "Laptop 20",
    },
    "laptop-21": {
      type: "laptop",
      label: "Laptop 21",
    },
    "laptop-22": {
      type: "laptop",
      label: "Laptop 22",
    },
    "laptop-23": {
      type: "laptop",
      label: "Laptop 23",
    },
    "laptop-24": {
      type: "laptop",
      label: "Laptop 24",
    },
    "laptop-25": {
      type: "laptop",
      label: "Laptop 25",
    },
    "laptop-26": {
      type: "laptop",
      label: "Laptop 26",
    },
    "laptop-27": {
      type: "laptop",
      label: "Laptop 27",
    },
    "laptop-28": {
      type: "laptop",
      label: "Laptop 28",
    },
    "laptop-29": {
      type: "laptop",
      label: "Laptop 29",
    },
    "laptop-30": {
      type: "laptop",
      label: "Laptop 30",
    },
    "laptop-31": {
      type: "laptop",
      label: "Laptop 31",
    },
    "laptop-32": {
      type: "laptop",
      label: "Laptop 32",
    },
    "laptop-33": {
      type: "laptop",
      label: "Laptop 33",
    },
    "laptop-34": {
      type: "laptop",
      label: "Laptop 34",
    },
    "laptop-35": {
      type: "laptop",
      label: "Laptop 35",
    },
    "laptop-36": {
      type: "laptop",
      label: "Laptop 36",
    },
    "laptop-37": {
      type: "laptop",
      label: "Laptop 37",
    },
    "laptop-38": {
      type: "laptop",
      label: "Laptop 38",
    },
    "laptop-39": {
      type: "laptop",
      label: "Laptop 39",
    },
    "laptop-40": {
      type: "laptop",
      label: "Laptop 40",
    },
    "laptop-41": {
      type: "laptop",
      label: "Laptop 41",
    },
    "laptop-42": {
      type: "laptop",
      label: "Laptop 42",
    },
    "laptop-43": {
      type: "laptop",
      label: "Laptop 43",
    },
    "laptop-44": {
      type: "laptop",
      label: "Laptop 44",
    },
    "laptop-45": {
      type: "laptop",
      label: "Laptop 45",
    },
    "laptop-46": {
      type: "laptop",
      label: "Laptop 46",
    },
    "laptop-47": {
      type: "laptop",
      label: "Laptop 47",
    },
    "laptop-48": {
      type: "laptop",
      label: "Laptop 48",
    },
    "laptop-49": {
      type: "laptop",
      label: "Laptop 49",
    },
    "laptop-50": {
      type: "laptop",
      label: "Laptop 50",
    },
    "laptop-51": {
      type: "laptop",
      label: "Laptop 51",
    },
    "laptop-52": {
      type: "laptop",
      label: "Laptop 52",
    },
    "laptop-53": {
      type: "laptop",
      label: "Laptop 53",
    },
    "laptop-54": {
      type: "laptop",
      label: "Laptop 54",
    },
    "laptop-55": {
      type: "laptop",
      label: "Laptop 55",
    },
    "laptop-56": {
      type: "laptop",
      label: "Laptop 56",
    },
    "laptop-57": {
      type: "laptop",
      label: "Laptop 57",
    },
    "laptop-58": {
      type: "laptop",
      label: "Laptop 58",
    },
    "laptop-59": {
      type: "laptop",
      label: "Laptop 59",
    },
    "laptop-60": {
      type: "laptop",
      label: "Laptop 60",
    },
    "laptop-61": {
      type: "laptop",
      label: "Laptop 61",
    },
    "laptop-62": {
      type: "laptop",
      label: "Laptop 62",
    },
    "laptop-63": {
      type: "laptop",
      label: "Laptop 63",
    },
    "laptop-64": {
      type: "laptop",
      label: "Laptop 64",
    },
    "laptop-65": {
      type: "laptop",
      label: "Laptop 65",
    },
    "laptop-66": {
      type: "laptop",
      label: "Laptop 66",
    },
    "laptop-67": {
      type: "laptop",
      label: "Laptop 67",
    },
    "laptop-68": {
      type: "laptop",
      label: "Laptop 68",
    },
    "laptop-69": {
      type: "laptop",
      label: "Laptop 69",
    },
    "laptop-70": {
      type: "laptop",
      label: "Laptop 70",
    },
    "laptop-71": {
      type: "laptop",
      label: "Laptop 71",
    },
    "laptop-72": {
      type: "laptop",
      label: "Laptop 72",
    },
    "laptop-73": {
      type: "laptop",
      label: "Laptop 73",
    },
    "laptop-74": {
      type: "laptop",
      label: "Laptop 74",
    },
    "laptop-75": {
      type: "laptop",
      label: "Laptop 75",
    },
    "laptop-76": {
      type: "laptop",
      label: "Laptop 76",
    },
    "laptop-77": {
      type: "laptop",
      label: "Laptop 77",
    },
    "laptop-78": {
      type: "laptop",
      label: "Laptop 78",
    },
    "laptop-79": {
      type: "laptop",
      label: "Laptop 79",
    },
    "laptop-80": {
      type: "laptop",
      label: "Laptop 80",
    },
    "laptop-81": {
      type: "laptop",
      label: "Laptop 81",
    },
    "laptop-82": {
      type: "laptop",
      label: "Laptop 82",
    },
    "laptop-83": {
      type: "laptop",
      label: "Laptop 83",
    },
    "laptop-84": {
      type: "laptop",
      label: "Laptop 84",
    },
    "laptop-85": {
      type: "laptop",
      label: "Laptop 85",
    },
    "laptop-86": {
      type: "laptop",
      label: "Laptop 86",
    },
    "laptop-87": {
      type: "laptop",
      label: "Laptop 87",
    },
    "laptop-88": {
      type: "laptop",
      label: "Laptop 88",
    },
    "laptop-89": {
      type: "laptop",
      label: "Laptop 89",
    },
    "laptop-90": {
      type: "laptop",
      label: "Laptop 90",
    },
    "laptop-91": {
      type: "laptop",
      label: "Laptop 91",
    },
    "laptop-92": {
      type: "laptop",
      label: "Laptop 92",
    },
    "laptop-93": {
      type: "laptop",
      label: "Laptop 93",
    },
    "laptop-94": {
      type: "laptop",
      label: "Laptop 94",
    },
    "laptop-95": {
      type: "laptop",
      label: "Laptop 95",
    },
    "laptop-96": {
      type: "laptop",
      label: "Laptop 96",
    },
    "laptop-97": {
      type: "laptop",
      label: "Laptop 97",
    },
    "laptop-98": {
      type: "laptop",
      label: "Laptop 98",
    },
    "laptop-99": {
      type: "laptop",
      label: "Laptop 99",
    },
    "laptop-100": {
      type: "laptop",
      label: "Laptop 100",
    },
    "laptop-101": {
      type: "laptop",
      label: "Laptop 101",
    },
    "laptop-102": {
      type: "laptop",
      label: "Laptop 102",
    },
    "laptop-103": {
      type: "laptop",
      label: "Laptop 103",
    },
    "laptop-104": {
      type: "laptop",
      label: "Laptop 104",
    },
    "laptop-105": {
      type: "laptop",
      label: "Laptop 105",
    },
    "laptop-106": {
      type: "laptop",
      label: "Laptop 106",
    },
    "laptop-107": {
      type: "laptop",
      label: "Laptop 107",
    },
    "laptop-108": {
      type: "laptop",
      label: "Laptop 108",
    },
    "laptop-109": {
      type: "laptop",
      label: "Laptop 109",
    },
    "laptop-110": {
      type: "laptop",
      label: "Laptop 110",
    },
    "laptop-111": {
      type: "laptop",
      label: "Laptop 111",
    },
    "laptop-112": {
      type: "laptop",
      label: "Laptop 112",
    },
    "laptop-113": {
      type: "laptop",
      label: "Laptop 113",
    },
    "laptop-114": {
      type: "laptop",
      label: "Laptop 114",
    },
    "laptop-115": {
      type: "laptop",
      label: "Laptop 115",
    },
    "laptop-116": {
      type: "laptop",
      label: "Laptop 116",
    },
    "laptop-117": {
      type: "laptop",
      label: "Laptop 117",
    },
    "laptop-118": {
      type: "laptop",
      label: "Laptop 118",
    },
    "laptop-119": {
      type: "laptop",
      label: "Laptop 119",
    },
    "laptop-120": {
      type: "laptop",
      label: "Laptop 120",
    },
    "laptop-121": {
      type: "laptop",
      label: "Laptop 121",
    },
    "laptop-122": {
      type: "laptop",
      label: "Laptop 122",
    },
    "laptop-123": {
      type: "laptop",
      label: "Laptop 123",
    },
    "laptop-124": {
      type: "laptop",
      label: "Laptop 124",
    },
    "laptop-125": {
      type: "laptop",
      label: "Laptop 125",
    },
    "laptop-126": {
      type: "laptop",
      label: "Laptop 126",
    },
    "laptop-127": {
      type: "laptop",
      label: "Laptop 127",
    },
    "laptop-128": {
      type: "laptop",
      label: "Laptop 128",
    },
    "laptop-129": {
      type: "laptop",
      label: "Laptop 129",
    },
    "laptop-130": {
      type: "laptop",
      label: "Laptop 130",
    },
    "laptop-131": {
      type: "laptop",
      label: "Laptop 131",
    },
    "laptop-132": {
      type: "laptop",
      label: "Laptop 132",
    },
    "laptop-133": {
      type: "laptop",
      label: "Laptop 133",
    },
    "laptop-134": {
      type: "laptop",
      label: "Laptop 134",
    },
    "laptop-135": {
      type: "laptop",
      label: "Laptop 135",
    },
    "laptop-136": {
      type: "laptop",
      label: "Laptop 136",
    },
    "laptop-137": {
      type: "laptop",
      label: "Laptop 137",
    },
    "laptop-138": {
      type: "laptop",
      label: "Laptop 138",
    },
    "laptop-139": {
      type: "laptop",
      label: "Laptop 139",
    },
    "laptop-140": {
      type: "laptop",
      label: "Laptop 140",
    },
    "laptop-141": {
      type: "laptop",
      label: "Laptop 141",
    },
    "laptop-142": {
      type: "laptop",
      label: "Laptop 142",
    },
    "laptop-143": {
      type: "laptop",
      label: "Laptop 143",
    },
    "laptop-144": {
      type: "laptop",
      label: "Laptop 144",
    },
    "laptop-145": {
      type: "laptop",
      label: "Laptop 145",
    },
    "laptop-146": {
      type: "laptop",
      label: "Laptop 146",
    },
    "laptop-147": {
      type: "laptop",
      label: "Laptop 147",
    },
    "laptop-148": {
      type: "laptop",
      label: "Laptop 148",
    },
    "laptop-149": {
      type: "laptop",
      label: "Laptop 149",
    },
    "laptop-150": {
      type: "laptop",
      label: "Laptop 150",
    },
    "laptop-151": {
      type: "laptop",
      label: "Laptop 151",
    },
    "laptop-152": {
      type: "laptop",
      label: "Laptop 152",
    },
    "laptop-153": {
      type: "laptop",
      label: "Laptop 153",
    },
    "laptop-154": {
      type: "laptop",
      label: "Laptop 154",
    },
    "laptop-155": {
      type: "laptop",
      label: "Laptop 155",
    },
    "laptop-156": {
      type: "laptop",
      label: "Laptop 156",
    },
    "laptop-157": {
      type: "laptop",
      label: "Laptop 157",
    },
    "laptop-158": {
      type: "laptop",
      label: "Laptop 158",
    },
    "laptop-159": {
      type: "laptop",
      label: "Laptop 159",
    },
    "laptop-160": {
      type: "laptop",
      label: "Laptop 160",
    },
    "laptop-161": {
      type: "laptop",
      label: "Laptop 161",
    },
    "laptop-162": {
      type: "laptop",
      label: "Laptop 162",
    },
    "laptop-163": {
      type: "laptop",
      label: "Laptop 163",
    },
    "laptop-164": {
      type: "laptop",
      label: "Laptop 164",
    },
    "laptop-165": {
      type: "laptop",
      label: "Laptop 165",
    },
    "laptop-166": {
      type: "laptop",
      label: "Laptop 166",
    },
    "laptop-167": {
      type: "laptop",
      label: "Laptop 167",
    },
    "laptop-168": {
      type: "laptop",
      label: "Laptop 168",
    },
    "laptop-169": {
      type: "laptop",
      label: "Laptop 169",
    },
    "laptop-170": {
      type: "laptop",
      label: "Laptop 170",
    },
    "laptop-171": {
      type: "laptop",
      label: "Laptop 171",
    },
    "laptop-172": {
      type: "laptop",
      label: "Laptop 172",
    },
    "laptop-173": {
      type: "laptop",
      label: "Laptop 173",
    },
    "laptop-174": {
      type: "laptop",
      label: "Laptop 174",
    },
    "laptop-175": {
      type: "laptop",
      label: "Laptop 175",
    },
    "laptop-176": {
      type: "laptop",
      label: "Laptop 176",
    },
    "laptop-177": {
      type: "laptop",
      label: "Laptop 177",
    },
    "laptop-178": {
      type: "laptop",
      label: "Laptop 178",
    },
    "laptop-179": {
      type: "laptop",
      label: "Laptop 179",
    },
    "laptop-180": {
      type: "laptop",
      label: "Laptop 180",
    },
    "laptop-181": {
      type: "laptop",
      label: "Laptop 181",
    },
    "laptop-182": {
      type: "laptop",
      label: "Laptop 182",
    },
    "laptop-183": {
      type: "laptop",
      label: "Laptop 183",
    },
    "laptop-184": {
      type: "laptop",
      label: "Laptop 184",
    },
    "laptop-185": {
      type: "laptop",
      label: "Laptop 185",
    },
    "laptop-186": {
      type: "laptop",
      label: "Laptop 186",
    },
    "laptop-187": {
      type: "laptop",
      label: "Laptop 187",
    },
    "laptop-188": {
      type: "laptop",
      label: "Laptop 188",
    },
    "laptop-189": {
      type: "laptop",
      label: "Laptop 189",
    },
    "laptop-190": {
      type: "laptop",
      label: "Laptop 190",
    },
    "laptop-191": {
      type: "laptop",
      label: "Laptop 191",
    },
    "laptop-192": {
      type: "laptop",
      label: "Laptop 192",
    },
    "laptop-193": {
      type: "laptop",
      label: "Laptop 193",
    },
    "laptop-194": {
      type: "laptop",
      label: "Laptop 194",
    },
    "laptop-195": {
      type: "laptop",
      label: "Laptop 195",
    },
    "laptop-196": {
      type: "laptop",
      label: "Laptop 196",
    },
    "laptop-197": {
      type: "laptop",
      label: "Laptop 197",
    },
    "laptop-198": {
      type: "laptop",
      label: "Laptop 198",
    },
    "laptop-199": {
      type: "laptop",
      label: "Laptop 199",
    },
    "laptop-200": {
      type: "laptop",
      label: "Laptop 200",
    },
    "laptop-201": {
      type: "laptop",
      label: "Laptop 201",
    },
    "laptop-202": {
      type: "laptop",
      label: "Laptop 202",
    },
    "laptop-203": {
      type: "laptop",
      label: "Laptop 203",
    },
    "laptop-204": {
      type: "laptop",
      label: "Laptop 204",
    },
    "laptop-205": {
      type: "laptop",
      label: "Laptop 205",
    },
    "laptop-206": {
      type: "laptop",
      label: "Laptop 206",
    },
    "laptop-207": {
      type: "laptop",
      label: "Laptop 207",
    },
    "laptop-208": {
      type: "laptop",
      label: "Laptop 208",
    },
    "laptop-209": {
      type: "laptop",
      label: "Laptop 209",
    },
    "laptop-210": {
      type: "laptop",
      label: "Laptop 210",
    },
    "laptop-211": {
      type: "laptop",
      label: "Laptop 211",
    },
    "laptop-212": {
      type: "laptop",
      label: "Laptop 212",
    },
    "laptop-213": {
      type: "laptop",
      label: "Laptop 213",
    },
    "laptop-214": {
      type: "laptop",
      label: "Laptop 214",
    },
    "laptop-215": {
      type: "laptop",
      label: "Laptop 215",
    },
    "laptop-216": {
      type: "laptop",
      label: "Laptop 216",
    },
    "laptop-217": {
      type: "laptop",
      label: "Laptop 217",
    },
    "laptop-218": {
      type: "laptop",
      label: "Laptop 218",
    },
    "laptop-219": {
      type: "laptop",
      label: "Laptop 219",
    },
    "laptop-220": {
      type: "laptop",
      label: "Laptop 220",
    },
    "laptop-221": {
      type: "laptop",
      label: "Laptop 221",
    },
    "laptop-222": {
      type: "laptop",
      label: "Laptop 222",
    },
    "laptop-223": {
      type: "laptop",
      label: "Laptop 223",
    },
    "laptop-224": {
      type: "laptop",
      label: "Laptop 224",
    },
    "laptop-225": {
      type: "laptop",
      label: "Laptop 225",
    },
    "laptop-226": {
      type: "laptop",
      label: "Laptop 226",
    },
    "laptop-227": {
      type: "laptop",
      label: "Laptop 227",
    },
    "laptop-228": {
      type: "laptop",
      label: "Laptop 228",
    },
    "laptop-229": {
      type: "laptop",
      label: "Laptop 229",
    },
    "laptop-230": {
      type: "laptop",
      label: "Laptop 230",
    },
    "laptop-231": {
      type: "laptop",
      label: "Laptop 231",
    },
    "laptop-232": {
      type: "laptop",
      label: "Laptop 232",
    },
    "laptop-233": {
      type: "laptop",
      label: "Laptop 233",
    },
    "laptop-234": {
      type: "laptop",
      label: "Laptop 234",
    },
    "laptop-235": {
      type: "laptop",
      label: "Laptop 235",
    },
    "laptop-236": {
      type: "laptop",
      label: "Laptop 236",
    },
    "laptop-237": {
      type: "laptop",
      label: "Laptop 237",
    },
    "laptop-238": {
      type: "laptop",
      label: "Laptop 238",
    },
    "laptop-239": {
      type: "laptop",
      label: "Laptop 239",
    },
    "laptop-240": {
      type: "laptop",
      label: "Laptop 240",
    },
    "laptop-241": {
      type: "laptop",
      label: "Laptop 241",
    },
    "laptop-242": {
      type: "laptop",
      label: "Laptop 242",
    },
    "laptop-243": {
      type: "laptop",
      label: "Laptop 243",

// ...truncated 27732 lines
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/public/fonts/fontawesome-free/css/all.css" />
    <link rel="stylesheet" href="@ci/theme/kg/css/examples.css" />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div class="story">
      <div class="story__timeline" id="my-timeline"></div>
      <div class="story__controls story__controls--row">
        <div class="stack">
          <label for="delay-select">Additional Loading Latency</label>
          <select id="delay-select" class="select">
            <option value="0" selected>None</option>
            <option value="500">500ms</option>
            <option value="1000">1000ms</option>
            <option value="2000">2000ms</option>
          </select>
        </div>
        <div class="stack">
          <p class="story__controls__heading" id="event-info"></p>
        </div>
      </div>
    </div>
    <script type="module" src="./code.js"></script>
  </body>
</html>
.story__loading__cursor canvas {
  cursor: progress !important;
}
import React, { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import Timeline from "kronograph/react/Timeline";
import data from "./data";

const oneWeek = 7 * 24 * 60 * 60 * 1000; // a week in milliseconds
const weeksToShow = 2;
const initialRange = oneWeek * weeksToShow;

const options = {
  events: {
    heatmapThreshold: 200,
  },
  scales: {
    panLimit: "off",
    zoomOutLimit: {
      unit: "week",
      value: weeksToShow,
    },
  },
};

const {
  metadata,
  entityTypes,
  eventTypes,
  entities,
  events: eventsArray,
} = data;

const firstEventTime = metadata.firstEventTime;
const lastEventTime = metadata.lastEventTime;
const dataTimeWidth = lastEventTime - firstEventTime;
const weeksOfDataToLoad = 4;

// convert to UTC milliseconds
// see https://kronograph.io/docs#milliseconds for details
function dateToMilliseconds(date) {
  return Date.UTC(
    date.getFullYear(),
    date.getMonth(),
    date.getDate(),
    date.getHours(),
    date.getMinutes(),
    date.getSeconds(),
    date.getMilliseconds(),
  );
}

export const Demo = () => {
  const [events, setEvents] = useState();
  const [eventCount, setEventCount] = useState(0);
  const [latency, setLatency] = useState(0);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    const { events: initialEvents, eventCount: initialEventCount } =
      getEventsWithBuffer(firstEventTime, firstEventTime + initialRange);
    setEvents(initialEvents);
    setEventCount(initialEventCount);
    setLoading(false);
  }, []);

  const getEventsWithBuffer = (start, end) => {
    const buffer = Math.floor(end - start) / 2;

    return getEvents(Math.max(start - buffer, firstEventTime), end + buffer);
  };

  const onTimelineChange = ({ range, why }) => {
    if (range !== undefined && why === "user") {
      const start = dateToMilliseconds(range.start);
      const end = dateToMilliseconds(range.end);

      setLoading(true);
      const { events: newEvents, eventCount: newEventCount } =
        getEventsWithBuffer(start, end);

      const loadData = () => {
        setEvents(newEvents);
        setEventCount(newEventCount);
        setLoading(false);
      };

      if (latency > 0) {
        setTimeout(loadData, latency);
      } else {
        loadData();
      }
    }
  };

  return (
    <div className="story">
      <Timeline
        className={`story__timeline ${loading && "story__loading__cursor"}`}
        entityTypes={entityTypes}
        eventTypes={eventTypes}
        entities={entities}
        events={events}
        options={options}
        onTimelineChange={onTimelineChange}
      />
      <div className="story__controls story__controls--row">
        <div className="stack">
          <label htmlFor="delay-select">Additional Loading Latency</label>
          <select
            className="select"
            value={latency}
            onChange={(e) => {
              setLatency(e.target.value);
            }}
          >
            <option value="0">None</option>
            <option value="500">500ms</option>
            <option value="1000">1000ms</option>
            <option value="2000">2000ms</option>
          </select>
        </div>
        <div className="stack">
          <p className="story__controls__heading">
            {`${eventCount.toLocaleString()} Events currently loaded`}
          </p>
        </div>
      </div>
    </div>
  );
};

/// /////////////////////////////////////////////////////////////////////////
// the code below is used to simulate fetching event data from a database //
/// /////////////////////////////////////////////////////////////////////////

function toIndex(items) {
  return items.reduce((result, item) => {
    const { id, ...itemWithoutId } = item;
    result[id] = itemWithoutId;
    return result;
  }, {});
}

function offsetEvents(eventArray, offset) {
  return eventArray.map((event) => {
    return {
      ...event,
      id: `${event.id}-${offset}`,
      time: event.time + dataTimeWidth * offset,
    };
  });
}

// simulate fetching data incrementally from a database
function getEvents(requestedStartTime, requestedEndTime) {
  const startTimeOffset = requestedStartTime - firstEventTime;
  const offsetFactor = Math.floor(startTimeOffset / dataTimeWidth);

  let newEvents = [];
  // a full weeks worth of data is loaded in and then repeated for enough weeks to fill the view range when fully zoomed out
  for (let i = 0; i < weeksOfDataToLoad; i++) {
    newEvents = newEvents.concat(offsetEvents(eventsArray, offsetFactor + i));
  }

  const firstEventIndex = newEvents.findIndex(
    (event) => event.time >= requestedStartTime,
  );

  const lastEventIndex = newEvents.findIndex(
    (event) => event.time >= requestedEndTime,
  );

  newEvents = newEvents.slice(firstEventIndex, lastEventIndex);

  return { events: toIndex(newEvents), eventCount: newEvents.length };
}

const root = createRoot(document.getElementById("my-timeline"));
root.render(<Demo />);
export default {
  metadata: {
    firstEventTime: 1578197001722.3477,
    lastEventTime: 1578852737532.8713,
  },
  entityTypes: {
    laptop: {
      typeLabel: "Client Devices",
    },
    server: {
      typeLabel: "Cloud Infrastructure",
    },
  },
  eventTypes: {
    logOn: {
      color: "#27ab83",
    },
    logOff: {
      color: "#f58a3d",
    },
    updateInstalled: {
      color: "#f7d06e",
    },
  },
  entities: {
    "laptop-0": {
      type: "laptop",
      label: "Laptop 0",
    },
    "laptop-1": {
      type: "laptop",
      label: "Laptop 1",
    },
    "laptop-2": {
      type: "laptop",
      label: "Laptop 2",
    },
    "laptop-3": {
      type: "laptop",
      label: "Laptop 3",
    },
    "laptop-4": {
      type: "laptop",
      label: "Laptop 4",
    },
    "laptop-5": {
      type: "laptop",
      label: "Laptop 5",
    },
    "laptop-6": {
      type: "laptop",
      label: "Laptop 6",
    },
    "laptop-7": {
      type: "laptop",
      label: "Laptop 7",
    },
    "laptop-8": {
      type: "laptop",
      label: "Laptop 8",
    },
    "laptop-9": {
      type: "laptop",
      label: "Laptop 9",
    },
    "laptop-10": {
      type: "laptop",
      label: "Laptop 10",
    },
    "laptop-11": {
      type: "laptop",
      label: "Laptop 11",
    },
    "laptop-12": {
      type: "laptop",
      label: "Laptop 12",
    },
    "laptop-13": {
      type: "laptop",
      label: "Laptop 13",
    },
    "laptop-14": {
      type: "laptop",
      label: "Laptop 14",
    },
    "laptop-15": {
      type: "laptop",
      label: "Laptop 15",
    },
    "laptop-16": {
      type: "laptop",
      label: "Laptop 16",
    },
    "laptop-17": {
      type: "laptop",
      label: "Laptop 17",
    },
    "laptop-18": {
      type: "laptop",
      label: "Laptop 18",
    },
    "laptop-19": {
      type: "laptop",
      label: "Laptop 19",
    },
    "laptop-20": {
      type: "laptop",
      label: "Laptop 20",
    },
    "laptop-21": {
      type: "laptop",
      label: "Laptop 21",
    },
    "laptop-22": {
      type: "laptop",
      label: "Laptop 22",
    },
    "laptop-23": {
      type: "laptop",
      label: "Laptop 23",
    },
    "laptop-24": {
      type: "laptop",
      label: "Laptop 24",
    },
    "laptop-25": {
      type: "laptop",
      label: "Laptop 25",
    },
    "laptop-26": {
      type: "laptop",
      label: "Laptop 26",
    },
    "laptop-27": {
      type: "laptop",
      label: "Laptop 27",
    },
    "laptop-28": {
      type: "laptop",
      label: "Laptop 28",
    },
    "laptop-29": {
      type: "laptop",
      label: "Laptop 29",
    },
    "laptop-30": {
      type: "laptop",
      label: "Laptop 30",
    },
    "laptop-31": {
      type: "laptop",
      label: "Laptop 31",
    },
    "laptop-32": {
      type: "laptop",
      label: "Laptop 32",
    },
    "laptop-33": {
      type: "laptop",
      label: "Laptop 33",
    },
    "laptop-34": {
      type: "laptop",
      label: "Laptop 34",
    },
    "laptop-35": {
      type: "laptop",
      label: "Laptop 35",
    },
    "laptop-36": {
      type: "laptop",
      label: "Laptop 36",
    },
    "laptop-37": {
      type: "laptop",
      label: "Laptop 37",
    },
    "laptop-38": {
      type: "laptop",
      label: "Laptop 38",
    },
    "laptop-39": {
      type: "laptop",
      label: "Laptop 39",
    },
    "laptop-40": {
      type: "laptop",
      label: "Laptop 40",
    },
    "laptop-41": {
      type: "laptop",
      label: "Laptop 41",
    },
    "laptop-42": {
      type: "laptop",
      label: "Laptop 42",
    },
    "laptop-43": {
      type: "laptop",
      label: "Laptop 43",
    },
    "laptop-44": {
      type: "laptop",
      label: "Laptop 44",
    },
    "laptop-45": {
      type: "laptop",
      label: "Laptop 45",
    },
    "laptop-46": {
      type: "laptop",
      label: "Laptop 46",
    },
    "laptop-47": {
      type: "laptop",
      label: "Laptop 47",
    },
    "laptop-48": {
      type: "laptop",
      label: "Laptop 48",
    },
    "laptop-49": {
      type: "laptop",
      label: "Laptop 49",
    },
    "laptop-50": {
      type: "laptop",
      label: "Laptop 50",
    },
    "laptop-51": {
      type: "laptop",
      label: "Laptop 51",
    },
    "laptop-52": {
      type: "laptop",
      label: "Laptop 52",
    },
    "laptop-53": {
      type: "laptop",
      label: "Laptop 53",
    },
    "laptop-54": {
      type: "laptop",
      label: "Laptop 54",
    },
    "laptop-55": {
      type: "laptop",
      label: "Laptop 55",
    },
    "laptop-56": {
      type: "laptop",
      label: "Laptop 56",
    },
    "laptop-57": {
      type: "laptop",
      label: "Laptop 57",
    },
    "laptop-58": {
      type: "laptop",
      label: "Laptop 58",
    },
    "laptop-59": {
      type: "laptop",
      label: "Laptop 59",
    },
    "laptop-60": {
      type: "laptop",
      label: "Laptop 60",
    },
    "laptop-61": {
      type: "laptop",
      label: "Laptop 61",
    },
    "laptop-62": {
      type: "laptop",
      label: "Laptop 62",
    },
    "laptop-63": {
      type: "laptop",
      label: "Laptop 63",
    },
    "laptop-64": {
      type: "laptop",
      label: "Laptop 64",
    },
    "laptop-65": {
      type: "laptop",
      label: "Laptop 65",
    },
    "laptop-66": {
      type: "laptop",
      label: "Laptop 66",
    },
    "laptop-67": {
      type: "laptop",
      label: "Laptop 67",
    },
    "laptop-68": {
      type: "laptop",
      label: "Laptop 68",
    },
    "laptop-69": {
      type: "laptop",
      label: "Laptop 69",
    },
    "laptop-70": {
      type: "laptop",
      label: "Laptop 70",
    },
    "laptop-71": {
      type: "laptop",
      label: "Laptop 71",
    },
    "laptop-72": {
      type: "laptop",
      label: "Laptop 72",
    },
    "laptop-73": {
      type: "laptop",
      label: "Laptop 73",
    },
    "laptop-74": {
      type: "laptop",
      label: "Laptop 74",
    },
    "laptop-75": {
      type: "laptop",
      label: "Laptop 75",
    },
    "laptop-76": {
      type: "laptop",
      label: "Laptop 76",
    },
    "laptop-77": {
      type: "laptop",
      label: "Laptop 77",
    },
    "laptop-78": {
      type: "laptop",
      label: "Laptop 78",
    },
    "laptop-79": {
      type: "laptop",
      label: "Laptop 79",
    },
    "laptop-80": {
      type: "laptop",
      label: "Laptop 80",
    },
    "laptop-81": {
      type: "laptop",
      label: "Laptop 81",
    },
    "laptop-82": {
      type: "laptop",
      label: "Laptop 82",
    },
    "laptop-83": {
      type: "laptop",
      label: "Laptop 83",
    },
    "laptop-84": {
      type: "laptop",
      label: "Laptop 84",
    },
    "laptop-85": {
      type: "laptop",
      label: "Laptop 85",
    },
    "laptop-86": {
      type: "laptop",
      label: "Laptop 86",
    },
    "laptop-87": {
      type: "laptop",
      label: "Laptop 87",
    },
    "laptop-88": {
      type: "laptop",
      label: "Laptop 88",
    },
    "laptop-89": {
      type: "laptop",
      label: "Laptop 89",
    },
    "laptop-90": {
      type: "laptop",
      label: "Laptop 90",
    },
    "laptop-91": {
      type: "laptop",
      label: "Laptop 91",
    },
    "laptop-92": {
      type: "laptop",
      label: "Laptop 92",
    },
    "laptop-93": {
      type: "laptop",
      label: "Laptop 93",
    },
    "laptop-94": {
      type: "laptop",
      label: "Laptop 94",
    },
    "laptop-95": {
      type: "laptop",
      label: "Laptop 95",
    },
    "laptop-96": {
      type: "laptop",
      label: "Laptop 96",
    },
    "laptop-97": {
      type: "laptop",
      label: "Laptop 97",
    },
    "laptop-98": {
      type: "laptop",
      label: "Laptop 98",
    },
    "laptop-99": {
      type: "laptop",
      label: "Laptop 99",
    },
    "laptop-100": {
      type: "laptop",
      label: "Laptop 100",
    },
    "laptop-101": {
      type: "laptop",
      label: "Laptop 101",
    },
    "laptop-102": {
      type: "laptop",
      label: "Laptop 102",
    },
    "laptop-103": {
      type: "laptop",
      label: "Laptop 103",
    },
    "laptop-104": {
      type: "laptop",
      label: "Laptop 104",
    },
    "laptop-105": {
      type: "laptop",
      label: "Laptop 105",
    },
    "laptop-106": {
      type: "laptop",
      label: "Laptop 106",
    },
    "laptop-107": {
      type: "laptop",
      label: "Laptop 107",
    },
    "laptop-108": {
      type: "laptop",
      label: "Laptop 108",
    },
    "laptop-109": {
      type: "laptop",
      label: "Laptop 109",
    },
    "laptop-110": {
      type: "laptop",
      label: "Laptop 110",
    },
    "laptop-111": {
      type: "laptop",
      label: "Laptop 111",
    },
    "laptop-112": {
      type: "laptop",
      label: "Laptop 112",
    },
    "laptop-113": {
      type: "laptop",
      label: "Laptop 113",
    },
    "laptop-114": {
      type: "laptop",
      label: "Laptop 114",
    },
    "laptop-115": {
      type: "laptop",
      label: "Laptop 115",
    },
    "laptop-116": {
      type: "laptop",
      label: "Laptop 116",
    },
    "laptop-117": {
      type: "laptop",
      label: "Laptop 117",
    },
    "laptop-118": {
      type: "laptop",
      label: "Laptop 118",
    },
    "laptop-119": {
      type: "laptop",
      label: "Laptop 119",
    },
    "laptop-120": {
      type: "laptop",
      label: "Laptop 120",
    },
    "laptop-121": {
      type: "laptop",
      label: "Laptop 121",
    },
    "laptop-122": {
      type: "laptop",
      label: "Laptop 122",
    },
    "laptop-123": {
      type: "laptop",
      label: "Laptop 123",
    },
    "laptop-124": {
      type: "laptop",
      label: "Laptop 124",
    },
    "laptop-125": {
      type: "laptop",
      label: "Laptop 125",
    },
    "laptop-126": {
      type: "laptop",
      label: "Laptop 126",
    },
    "laptop-127": {
      type: "laptop",
      label: "Laptop 127",
    },
    "laptop-128": {
      type: "laptop",
      label: "Laptop 128",
    },
    "laptop-129": {
      type: "laptop",
      label: "Laptop 129",
    },
    "laptop-130": {
      type: "laptop",
      label: "Laptop 130",
    },
    "laptop-131": {
      type: "laptop",
      label: "Laptop 131",
    },
    "laptop-132": {
      type: "laptop",
      label: "Laptop 132",
    },
    "laptop-133": {
      type: "laptop",
      label: "Laptop 133",
    },
    "laptop-134": {
      type: "laptop",
      label: "Laptop 134",
    },
    "laptop-135": {
      type: "laptop",
      label: "Laptop 135",
    },
    "laptop-136": {
      type: "laptop",
      label: "Laptop 136",
    },
    "laptop-137": {
      type: "laptop",
      label: "Laptop 137",
    },
    "laptop-138": {
      type: "laptop",
      label: "Laptop 138",
    },
    "laptop-139": {
      type: "laptop",
      label: "Laptop 139",
    },
    "laptop-140": {
      type: "laptop",
      label: "Laptop 140",
    },
    "laptop-141": {
      type: "laptop",
      label: "Laptop 141",
    },
    "laptop-142": {
      type: "laptop",
      label: "Laptop 142",
    },
    "laptop-143": {
      type: "laptop",
      label: "Laptop 143",
    },
    "laptop-144": {
      type: "laptop",
      label: "Laptop 144",
    },
    "laptop-145": {
      type: "laptop",
      label: "Laptop 145",
    },
    "laptop-146": {
      type: "laptop",
      label: "Laptop 146",
    },
    "laptop-147": {
      type: "laptop",
      label: "Laptop 147",
    },
    "laptop-148": {
      type: "laptop",
      label: "Laptop 148",
    },
    "laptop-149": {
      type: "laptop",
      label: "Laptop 149",
    },
    "laptop-150": {
      type: "laptop",
      label: "Laptop 150",
    },
    "laptop-151": {
      type: "laptop",
      label: "Laptop 151",
    },
    "laptop-152": {
      type: "laptop",
      label: "Laptop 152",
    },
    "laptop-153": {
      type: "laptop",
      label: "Laptop 153",
    },
    "laptop-154": {
      type: "laptop",
      label: "Laptop 154",
    },
    "laptop-155": {
      type: "laptop",
      label: "Laptop 155",
    },
    "laptop-156": {
      type: "laptop",
      label: "Laptop 156",
    },
    "laptop-157": {
      type: "laptop",
      label: "Laptop 157",
    },
    "laptop-158": {
      type: "laptop",
      label: "Laptop 158",
    },
    "laptop-159": {
      type: "laptop",
      label: "Laptop 159",
    },
    "laptop-160": {
      type: "laptop",
      label: "Laptop 160",
    },
    "laptop-161": {
      type: "laptop",
      label: "Laptop 161",
    },
    "laptop-162": {
      type: "laptop",
      label: "Laptop 162",
    },
    "laptop-163": {
      type: "laptop",
      label: "Laptop 163",
    },
    "laptop-164": {
      type: "laptop",
      label: "Laptop 164",
    },
    "laptop-165": {
      type: "laptop",
      label: "Laptop 165",
    },
    "laptop-166": {
      type: "laptop",
      label: "Laptop 166",
    },
    "laptop-167": {
      type: "laptop",
      label: "Laptop 167",
    },
    "laptop-168": {
      type: "laptop",
      label: "Laptop 168",
    },
    "laptop-169": {
      type: "laptop",
      label: "Laptop 169",
    },
    "laptop-170": {
      type: "laptop",
      label: "Laptop 170",
    },
    "laptop-171": {
      type: "laptop",
      label: "Laptop 171",
    },
    "laptop-172": {
      type: "laptop",
      label: "Laptop 172",
    },
    "laptop-173": {
      type: "laptop",
      label: "Laptop 173",
    },
    "laptop-174": {
      type: "laptop",
      label: "Laptop 174",
    },
    "laptop-175": {
      type: "laptop",
      label: "Laptop 175",
    },
    "laptop-176": {
      type: "laptop",
      label: "Laptop 176",
    },
    "laptop-177": {
      type: "laptop",
      label: "Laptop 177",
    },
    "laptop-178": {
      type: "laptop",
      label: "Laptop 178",
    },
    "laptop-179": {
      type: "laptop",
      label: "Laptop 179",
    },
    "laptop-180": {
      type: "laptop",
      label: "Laptop 180",
    },
    "laptop-181": {
      type: "laptop",
      label: "Laptop 181",
    },
    "laptop-182": {
      type: "laptop",
      label: "Laptop 182",
    },
    "laptop-183": {
      type: "laptop",
      label: "Laptop 183",
    },
    "laptop-184": {
      type: "laptop",
      label: "Laptop 184",
    },
    "laptop-185": {
      type: "laptop",
      label: "Laptop 185",
    },
    "laptop-186": {
      type: "laptop",
      label: "Laptop 186",
    },
    "laptop-187": {
      type: "laptop",
      label: "Laptop 187",
    },
    "laptop-188": {
      type: "laptop",
      label: "Laptop 188",
    },
    "laptop-189": {
      type: "laptop",
      label: "Laptop 189",
    },
    "laptop-190": {
      type: "laptop",
      label: "Laptop 190",
    },
    "laptop-191": {
      type: "laptop",
      label: "Laptop 191",
    },
    "laptop-192": {
      type: "laptop",
      label: "Laptop 192",
    },
    "laptop-193": {
      type: "laptop",
      label: "Laptop 193",
    },
    "laptop-194": {
      type: "laptop",
      label: "Laptop 194",
    },
    "laptop-195": {
      type: "laptop",
      label: "Laptop 195",
    },
    "laptop-196": {
      type: "laptop",
      label: "Laptop 196",
    },
    "laptop-197": {
      type: "laptop",
      label: "Laptop 197",
    },
    "laptop-198": {
      type: "laptop",
      label: "Laptop 198",
    },
    "laptop-199": {
      type: "laptop",
      label: "Laptop 199",
    },
    "laptop-200": {
      type: "laptop",
      label: "Laptop 200",
    },
    "laptop-201": {
      type: "laptop",
      label: "Laptop 201",
    },
    "laptop-202": {
      type: "laptop",
      label: "Laptop 202",
    },
    "laptop-203": {
      type: "laptop",
      label: "Laptop 203",
    },
    "laptop-204": {
      type: "laptop",
      label: "Laptop 204",
    },
    "laptop-205": {
      type: "laptop",
      label: "Laptop 205",
    },
    "laptop-206": {
      type: "laptop",
      label: "Laptop 206",
    },
    "laptop-207": {
      type: "laptop",
      label: "Laptop 207",
    },
    "laptop-208": {
      type: "laptop",
      label: "Laptop 208",
    },
    "laptop-209": {
      type: "laptop",
      label: "Laptop 209",
    },
    "laptop-210": {
      type: "laptop",
      label: "Laptop 210",
    },
    "laptop-211": {
      type: "laptop",
      label: "Laptop 211",
    },
    "laptop-212": {
      type: "laptop",
      label: "Laptop 212",
    },
    "laptop-213": {
      type: "laptop",
      label: "Laptop 213",
    },
    "laptop-214": {
      type: "laptop",
      label: "Laptop 214",
    },
    "laptop-215": {
      type: "laptop",
      label: "Laptop 215",
    },
    "laptop-216": {
      type: "laptop",
      label: "Laptop 216",
    },
    "laptop-217": {
      type: "laptop",
      label: "Laptop 217",
    },
    "laptop-218": {
      type: "laptop",
      label: "Laptop 218",
    },
    "laptop-219": {
      type: "laptop",
      label: "Laptop 219",
    },
    "laptop-220": {
      type: "laptop",
      label: "Laptop 220",
    },
    "laptop-221": {
      type: "laptop",
      label: "Laptop 221",
    },
    "laptop-222": {
      type: "laptop",
      label: "Laptop 222",
    },
    "laptop-223": {
      type: "laptop",
      label: "Laptop 223",
    },
    "laptop-224": {
      type: "laptop",
      label: "Laptop 224",
    },
    "laptop-225": {
      type: "laptop",
      label: "Laptop 225",
    },
    "laptop-226": {
      type: "laptop",
      label: "Laptop 226",
    },
    "laptop-227": {
      type: "laptop",
      label: "Laptop 227",
    },
    "laptop-228": {
      type: "laptop",
      label: "Laptop 228",
    },
    "laptop-229": {
      type: "laptop",
      label: "Laptop 229",
    },
    "laptop-230": {
      type: "laptop",
      label: "Laptop 230",
    },
    "laptop-231": {
      type: "laptop",
      label: "Laptop 231",
    },
    "laptop-232": {
      type: "laptop",
      label: "Laptop 232",
    },
    "laptop-233": {
      type: "laptop",
      label: "Laptop 233",
    },
    "laptop-234": {
      type: "laptop",
      label: "Laptop 234",
    },
    "laptop-235": {
      type: "laptop",
      label: "Laptop 235",
    },
    "laptop-236": {
      type: "laptop",
      label: "Laptop 236",
    },
    "laptop-237": {
      type: "laptop",
      label: "Laptop 237",
    },
    "laptop-238": {
      type: "laptop",
      label: "Laptop 238",
    },
    "laptop-239": {
      type: "laptop",
      label: "Laptop 239",
    },
    "laptop-240": {
      type: "laptop",
      label: "Laptop 240",
    },
    "laptop-241": {
      type: "laptop",
      label: "Laptop 241",
    },
    "laptop-242": {
      type: "laptop",
      label: "Laptop 242",
    },
    "laptop-243": {
      type: "laptop",
      label: "Laptop 243",

// ...truncated 27732 lines
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="@ci/theme/kg/css/examples.css" />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="my-timeline" style="height: 100vh"></div>
    <script type="module" src="./code.jsx"></script>
  </body>
</html>
.story__loading__cursor canvas {
  cursor: progress !important;
}

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.