Skip to content

Lists And Trees

Joao Moreno edited this page Oct 23, 2018 · 15 revisions

List

In its core, the list is a virtual rendering engine. Given a collection of elements, it will let you render those elements in a scrollable view, while making sure only the visible elements actually end up in the DOM, at any given point in time. Avoiding to render all the elements in the DOM a priori is the essence behind the list's performance. You can easily add 100k elements to it and not even break a sweat.

In order to use it, you must specify the height of every element in pixels before it gets rendered on the screen, by implementing the IListVirtualDelegate interface. At runtime, the list will keep an in-memory map of each element's height and track the viewport's position in order to know which elements should be rendered in and out of the DOM. Note that item heights can be variable, though A renderer needs to implement the IListRenderer interface.

Given that the collection model for a list is a simple array, the API to modify a list's element collection is very simple. The splice call allows for deleting and inserting continuous items in the list:

class List<T> {
  splice(start: number, deleteCount: number, elements: T[]): void
}

The List widget provides quite a bit of UX functionality on top of the basic rendering engine: keyboard and mouse navigation, focus and selection traits, accessibility roles, etc. These features are what defines List as a usable widget across our workbench.

Index Tree

Built on top of list.

Conceptually:

class IndexTree<T> {
  splice(location: number[], deleteCount: number, toInsert: T[]): T[];
}

Provides:

  • Expand, collapse functionality
  • Filter functionality

Object Tree

Built on top of index tree.

Conceptually:

class ObjectTree<T> {
  setChildren(element: T | null, children: T[]): T[];
}

Provides:

  • Object-addressable locations, easier API

Data Tree

Built on top of object tree.

Conceptually:

interface IDataSource<T> {
  hasChildren(element: T | null): boolean;
  getChildren(element: T | null): Thenable<T[]>;
}

class DataTree<T> {
  constructor(dataSource: IDataSource<T>);
  refresh(element: T | null): Thenable<void>;
}

Provides:

  • Automatic resolution of conflicting refresh calls
  • Loading twistie

Paged List

Build on top of list.

export interface IPagedModel<T> {
  length: number;
  isResolved(index: number): boolean;
  get(index: number): T;
  resolve(index: number): Thenable<T>;
}

class PagedList<T> {
  model: IPagedModel<T>;
}
Clone this wiki locally