Lattice Grid Buy a licence

api reference

Dashboard API Reference

createDashboard: a dashboard described as a JSON spec, a layout, grid, chart, KPI, map and html panels, a source per panel, links between them, built into the wired page, handed back by spec(), and proposed by the AI.

API reference › Dashboard composition

All 19 pages Everything on one page → Developer guide →

Dashboard composition

modules/dashboard describes a dashboard as data. One JSON spec names a layout, the panels in it - a grid, a chart, a KPI panel, a map or static html - the source each panel reads and the links between them, and createDashboard(el, spec, factories) builds the wired page. dashboard.spec() hands the same spec back, with every window where the user has since dragged it, so a host can store, diff and version a dashboard like any other document. It is a composition layer and nothing more: there is no builder UI, no persistence and no users, and everything it draws is drawn by a module you already have.

import { createGrid, createHeadlessGrid } from '@toclocoinc/lattice-grid';
import { createLayout } from '@toclocoinc/lattice-grid/modules/layout';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { createDashboard } from '@toclocoinc/lattice-grid/modules/dashboard';

const dashboard = createDashboard(document.querySelector('#dash'), {
  layout: { columns: 2, rows: 2 },
  sources: { sales: { kind: 'rows', rows, rowKey: 'id' } },
  panels: [
    { id: 'table', kind: 'grid', title: 'Sales', source: 'sales', options: { selection: 'multiple' } },
    { id: 'byRegion', kind: 'chart', source: 'grid:table', options: { type: 'bar', x: 'region', y: 'sales' } },
  ],
}, { createGrid, createHeadlessGrid, createLayout, createChart });

The viewers are handed in, never imported. The third argument names the factories the dashboard builds with - createGrid, createHeadlessGrid, createPushdownSource, createLayout, createChart, createKPI, bindLeaflet, bindDeck - so the module stays a few kilobytes and carries no second copy of any of them. On a script-tag page any factory not named is found on its own global (LatticeGrid, LatticeGridLayout, LatticeGridKPI, LatticeGridLeaflet, LatticeGridDeck). A panel whose factory is on neither is refused by name and the rest of the dashboard is built; without createLayout nothing is.

Spec keyWhat it says
layoutAny createLayout option (columns, rows, gap, compact, movable, …), handed to the layout as written, plus windows: one entry per panel id with its placement (xPos, yPos, xSize, ySize) and any window option. A panel with no entry is auto-placed; a panel's title names its window when the entry gives none.
panels[]{ id, kind, title, source, binding, options }. kind is grid, chart, kpi, map or html. options go to the viewer's own call unchanged - createGrid, createChart, createKPI, bindLeaflet or bindDeck - and the dashboard adds only the element and the grid. A map panel draws through a map chart (binding: 'chart', the default: markermap, choropleth, …), 'leaflet' or 'deckgl'; a Leaflet panel's options.map (a deck.gl panel's options.deck) may be a function of the panel's element that makes one. An html panel takes { html } - your own markup, set as it is - or { text }.
sourcesNamed sources: { kind: 'rows', rows }, { kind: 'pushdown', adapter, ... } (every other key is a createPushdownSource option), { kind: 'router', router, predicate, route } (each grid reading it is attached with router.attach(grid, predicate, route); load the router after the dashboard is built), or { kind: 'grid', of: '<panel id>' }. Each may carry the columns and rowKey every grid over it is given; a rows source with no columns gets one per key of its first rows. A panel names one as source: '<id>', or source: 'grid:<panel id>' to view that grid panel's grid.
links[]{ from, to, on }: the rows selected in the from grid panel filter the to panel. on is a column both share, or { from, to } naming each side's column.

A source is a grid. A grid panel is the grid over its source. A chart, KPI or map panel over a source gets a headless grid of its own, so two panels on different sources - or on the same one - stay independent until a link says otherwise. grid:<panel id> makes a panel a viewer of that panel's grid instead: filter the table and the chart over it narrows, exactly as a chart bound to a grid always has.

Links become what already exists. When both panels' grids are routes of one Data Router, a link is a router.relate() edge (and on may be the router's own relation function). Otherwise it is a named filters.where() predicate on the target's grid, rewritten on every selection:changed of the source grid and cleared with the selection - with a pushed-down in condition when the target reads a pushdown engine. Two pushdown sources with no router between them, two different routers, and a relation function without a router are refused by name, with the reason.

The AI proposes; you apply. With an AI callback in the options - the same ask(payload) createAI takes - dashboard.propose(prompt, { source }) asks for a spec over one source. The model is sent the source's column names and types and the panel kinds this page can draw, never a row unless you pass rows: true (at most maxRows, default 50). The answer is checked against the spec schema and the source's columns: a panel naming a column the source does not have, a kind the page cannot draw or another source is dropped, and each drop is a line in warnings; markup a model put in an html panel is removed. Nothing is built until you call dashboard.apply(spec). Without a callback, propose declines by name.

Views and presentation. saveView(name) stores the spec and every grid panel's grid.state.get() as one view; applyView(id) - or a view your application stored - puts the layout back with layout.setLayout() and each grid's state with grid.state.apply(), rebuilding first when the view's panels differ. present() steps through the panels with the layout's own maximise(); step(by) moves, stopPresenting() puts the dashboard back.

Refused by name. Every refusal is a line in dashboard.problems() ({ id, path, message }) and a console warning under a catalogued id: dashboard:panel:<id> for a panel that was not built, or was built but its viewer refused one of its options (heard from the viewer's own [lattice] warning, which quotes the option); dashboard:link:<from>-><to>; dashboard:spec:<path> for any other problem the validator finds; and dashboard:spec, dashboard:el, dashboard:layout when nothing could be built. validateDashboardSpec(spec) runs the same checks without building anything. destroy() removes the links, every viewer, grid and router attachment the dashboard made, and the layout.

Five panels from one spec, a chart narrowing with its grid, spec() rebuilding the same dashboard, and destroy

const { createTestDom, flushFrames } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid, createHeadlessGrid } = await import('../packages/dom/src/index.js');
const { createLayout } = await import('../packages/modules/layout/index.js');
const { createChart } = await import('../packages/modules/charts/index.js');
await import('../packages/modules/chart-markermap/index.js');
const { createKPI } = await import('../packages/modules/kpi/index.js');
const { createDashboard } = await import('../packages/modules/dashboard/index.js');

const { document, root } = createTestDom({ width: 1200, height: 800 });
const el = document.createElement('div');
root.appendChild(el);

const rows = Array.from({ length: 12 }, (_, i) => ({
  id: 'r' + i, region: ['north', 'south', 'east'][i % 3], sales: 10 * (i + 1),
  lon: -1 + i * 0.1, lat: 50 + i * 0.1,
}));

// Five panels, one source. The factories are handed in (on a script-tag page
// they are found on LatticeGrid, LatticeGridLayout and LatticeGridKPI).
const spec = {
  layout: { columns: 3, rows: 2 },
  sources: { sales: { kind: 'rows', rows, rowKey: 'id' } },
  panels: [
    { id: 'table', kind: 'grid', title: 'Sales', source: 'sales', options: { selection: 'multiple' } },
    { id: 'byRegion', kind: 'chart', source: 'grid:table', options: { type: 'bar', x: 'region', y: 'sales' } },
    { id: 'total', kind: 'kpi', source: 'grid:table', options: { tiles: [{ id: 'sum', field: 'sales', aggregation: 'sum' }] } },
    { id: 'where', kind: 'map', source: 'sales', options: { type: 'markermap', lon: 'lon', lat: 'lat' } },
    { id: 'note', kind: 'html', options: { text: 'Provisional figures.' } },
  ],
};
const dashboard = createDashboard(el, spec, { createGrid, createHeadlessGrid, createLayout, createChart, createKPI });
const seen = [dashboard.panels().length, dashboard.problems().length];

// The chart views the table's grid, so filtering the table narrows it.
dashboard.panel('table').grid.filters.set({ col: 'region', op: 'eq', value: 'north' });
flushFrames();                                        // the next frame
seen.push(dashboard.panel('byRegion').viewer.data().categories.join('+'));

// spec() is what is on screen, placements included; it rebuilds the same dashboard.
const once = dashboard.spec();
dashboard.apply(once);
seen.push(JSON.stringify(dashboard.spec()) === JSON.stringify(once));

dashboard.destroy();                                 // every viewer, grid and window
seen.push(el.children.length);
return seen.join(' | ');                             // 5 | 0 | north | true | 0

Mixed sources, the Leaflet and deck.gl bindings, a selection filtering a chart, and a link refused by name

const { createTestDom, flushFrames } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid, createHeadlessGrid, createPushdownSource } = await import('../packages/dom/src/index.js');
const { createLayout } = await import('../packages/modules/layout/index.js');
const { createChart } = await import('../packages/modules/charts/index.js');
const { bindLeaflet } = await import('../packages/modules/leaflet/index.js');
const { bindDeck } = await import('../packages/modules/deckgl/index.js');
const { createDashboard, validateDashboardSpec } = await import('../packages/modules/dashboard/index.js');

const { document, root } = createTestDom({ width: 1200, height: 800 });
const el = document.createElement('div');
root.appendChild(el);
const rows = Array.from({ length: 12 }, (_, i) => ({
  id: 'r' + i, region: ['north', 'south', 'east'][i % 3], sales: 10 * (i + 1), lon: -1 + i * 0.1, lat: 50 + i * 0.1,
}));
// Two engines with no router between them: stand-ins for two duckdbAdapter()s.
const engine = () => ({ capabilities: { filter: 'tree' }, execute: async () => ({ rows: [], total: 0 }) });
// Stand-ins for the page's L.map and deck.gl Deck.
const map = { addLayer() {}, removeLayer() {}, on() {}, off() {},
  getBounds: () => ({ getWest: () => -180, getSouth: () => -85, getEast: () => 180, getNorth: () => 85 }) };
const deck = { setProps() {} };

const spec = {
  sources: {
    sales: { kind: 'rows', rows, rowKey: 'id' },
    east: { kind: 'pushdown', adapter: engine(), rowKey: 'id', columns: [{ field: 'id' }, { field: 'region' }] },
    west: { kind: 'pushdown', adapter: engine(), rowKey: 'id', columns: [{ field: 'id' }, { field: 'region' }] },
  },
  panels: [
    { id: 'table', kind: 'grid', source: 'sales', options: { selection: 'multiple' } },
    { id: 'chart', kind: 'chart', source: 'sales', options: { type: 'bar', x: 'region', y: 'sales' } },
    { id: 'leaf', kind: 'map', binding: 'leaflet', source: 'sales', options: { map, layers: () => [], position: { lon: 'lon', lat: 'lat' } } },
    { id: 'gl', kind: 'map', binding: 'deckgl', source: 'sales', options: { deck, layers: () => [], position: { lon: 'lon', lat: 'lat' } } },
    { id: 'a', kind: 'grid', source: 'east' },
    { id: 'b', kind: 'grid', source: 'west' },
  ],
  links: [
    { from: 'table', to: 'chart', on: 'region' },    // the table's selection filters the chart
    { from: 'a', to: 'b', on: 'region' },            // refused: two engines, no shared router
  ],
};
const seen = [validateDashboardSpec(spec).ok];
const dashboard = createDashboard(el, spec, {
  createGrid, createHeadlessGrid, createPushdownSource, createLayout, createChart, bindLeaflet, bindDeck,
});
seen.push(dashboard.panels().length);

dashboard.panel('table').grid.selection.set(['r1']);   // a south row
flushFrames();                                        // the next frame
seen.push(dashboard.panel('chart').viewer.data().categories.join('+'));
seen.push(dashboard.problems().map((p) => p.path).join(', '));   // dashboard:link:a->b, at links[1]

// The validator names every problem by path, without building anything.
seen.push(validateDashboardSpec({ panels: [{ id: 'x', kind: 'gauge' }] }).problems[0].path);
dashboard.destroy();
return seen.join(' | ');   // true | 6 | south | links[1] | panels[0].kind

A proposal over a source, checked and applied; a view saved and restored; a presentation stepped

const { createTestDom, flushFrames } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid, createHeadlessGrid } = await import('../packages/dom/src/index.js');
const { createLayout } = await import('../packages/modules/layout/index.js');
const { createChart } = await import('../packages/modules/charts/index.js');
const { createDashboard } = await import('../packages/modules/dashboard/index.js');

const { document, root } = createTestDom({ width: 1200, height: 800 });
const el = document.createElement('div');
root.appendChild(el);
const rows = Array.from({ length: 12 }, (_, i) => ({
  id: 'r' + i, region: ['north', 'south', 'east'][i % 3], sales: 10 * (i + 1),
}));

// The same ask() createAI takes. It sees the columns and the panel kinds, never
// the rows. A stand-in model answers with a table, a chart and a column that
// does not exist.
let asked = null;
const ask = async (payload) => {
  asked = payload.schema;
  return { structured: { panels: [
    { id: 'top', kind: 'grid', source: 'sales', options: { selection: 'single' } },
    { id: 'byRegion', kind: 'chart', source: 'grid:top', options: { type: 'bar', x: 'region', y: 'sales' } },
    { id: 'trend', kind: 'chart', source: 'sales', options: { type: 'line', x: 'month', y: 'sales' } },
  ] } };
};
const dashboard = createDashboard(el, { sources: { sales: { kind: 'rows', rows, rowKey: 'id' } }, panels: [] },
  { createGrid, createHeadlessGrid, createLayout, createChart, ai: { ask } });

const { spec, warnings } = await dashboard.propose('sales by region and a table', { source: 'sales' });
const seen = [asked.columns.map((c) => c.id).join(','), spec.panels.length, warnings.length];  // 'trend' named no column
dashboard.apply(spec);                                  // accepted: now it is built
flushFrames();

// A view is the spec plus every grid panel's state.
dashboard.panel('top').grid.filters.set({ col: 'region', op: 'eq', value: 'east' });
const view = dashboard.saveView('East only');
dashboard.panel('top').grid.filters.clear();
dashboard.applyView(view.id);
seen.push(dashboard.views().length, dashboard.panel('top').grid.rows.matchCount());

// Presentation steps through the panels, each filling the dashboard.
dashboard.present();
dashboard.step();
seen.push(dashboard.layout.maximised());
dashboard.stopPresenting();
dashboard.destroy();
return seen.join(' | ');                              // id,region,sales | 2 | 1 | 1 | 4 | byRegion

Type reference

Generated from the type declarations, so it always matches the release. Each surface lists its properties, its methods and the events it raises as three tables; an option or value type lists its members once.

Dashboard composition

DashboardSpec

A dashboard described as data: a layout, the panels in it, the sources they read and the links between them. `createDashboard` builds it and `dashboard.spec()` hands it back, placements included, so a host can store and version it.

PropertyTypeDescription
layoutDashboardLayoutSpecThe layout the panels sit in: any `createLayout` option, with `windows` placing panels by id (`{ id, xPos, yPos, xSize, ySize, … }`). A panel with no window entry is auto-placed. (optional)
panelsDashboardPanel[]The panels, one viewer each, in the order their windows are mounted.
sourcesRecord<string, DashboardSource>The named sources panels read, by id. (optional)
linksDashboardLink[]Selection-to-filter links between panels. (optional)

DashboardLayoutSpec

The layout part of a spec: the layout module's own options, windows placed by panel id.

PropertyTypeDescription
columnsnumberCell columns across the dashboard (the layout's default otherwise). (optional)
rowsnumberCell rows down the dashboard (the layout's default otherwise). (optional)
gapnumber | stringThe gap between cells. (optional)
windowsArray<{ id: string; xPos?: number; yPos?: number; xSize?: number; ySize?: number; [option: string]: unknown }>Each panel's window, by panel id: its placement and any window option (title, chrome, movable, …). (optional)
[option: string]unknownEvery other key is handed to `createLayout` unchanged.

DashboardPanel

One panel: a viewer in a window.

PropertyTypeDescription
idstringA unique id; its window has the same id.
kindDashboardPanelKind 'grid' | 'chart' | 'kpi' | 'map' | 'html'Which viewer: a grid, a chart, a KPI panel, a map, or static html.
titlestringThe window's title, when its window entry declares none. (optional)
sourcestringThe source it reads: a key of `sources`, or `grid:<panel id>` to view that grid panel's grid (it narrows when that grid is filtered). Required for `chart` and `map`; an `html` panel reads none. (optional)
binding'chart' | 'leaflet' | 'deckgl'For a `map` panel, what draws it: `'chart'` (a map chart such as `markermap` or `choropleth`, the default), `'leaflet'` (`bindLeaflet`) or `'deckgl'` (`bindDeck`), each needing its module on the page. (optional)
optionsRecord<string, unknown>The viewer's own options, passed to `createGrid`, `createChart`, `createKPI`, `bindLeaflet` or `bindDeck` unchanged - the dashboard adds only the element and the grid. A Leaflet panel's `map` and a deck.gl panel's `deck` may be a function of the panel's element that makes one. An `html` panel takes `{ html }` (the host's own markup) or `{ text }`. (optional)

DashboardRowsSource

Rows held in the page.

PropertyTypeDescription
kind'rows'Rows held in the page.
rowsunknown[]The rows.
columnsunknown[]The grid columns; one per key of the first rows when omitted. (optional)
rowKeyunknownThe row identity every grid over this source is given. (optional)

DashboardPushdownSource

A pushdown adapter: the rows stay in the engine.

PropertyTypeDescription
kind'pushdown'A pushdown adapter: the rows stay in the engine.
adapter{ execute: (...args: unknown[]) => unknown; [key: string]: unknown }The adapter, handed to `createPushdownSource` with every key but `kind`, `columns` and `rowKey`.
columnsunknown[]The grid columns. (optional)
rowKeyunknownThe row identity every grid over this source is given. (optional)
[option: string]unknownAny other `createPushdownSource` option (`pageSize`, `aggregates`, …).

DashboardRouterSource

A Data Router route: each grid reading it is attached with `router.attach(grid, predicate, route)`.

PropertyTypeDescription
kind'router'A Data Router route.
router{ attach: (...args: unknown[]) => unknown; [key: string]: unknown }The router. Load it after the dashboard is built: a route sees what arrives after it is attached.
predicateunknownThe partition value or predicate the route matches.
routeRecord<string, unknown>The route's options (`transform`, `rollup`, …). (optional)
columnsunknown[]The grid columns. (optional)
rowKeyunknownThe row identity every grid over this source is given. (optional)

DashboardGridSource

Another panel's grid, as `grid:<panel id>` does inline.

PropertyTypeDescription
kind'grid'Another panel's grid.
ofstringThe grid panel whose grid is viewed.

A link: the rows selected in the `from` grid panel filter the `to` panel. Over one Data Router it is a `router.relate()` edge; otherwise a named `filters.where()` predicate on the target's grid. Two pushdown sources with no router between them, or two routers, are refused by name.

PropertyTypeDescription
fromstringThe grid panel whose selection drives the link.
tostringThe panel it filters.
onstring | { from: string; to: string } | ((selected: Array<Record<string, unknown>>) => (row: Record<string, unknown>) => boolean)The relation: a column both sides share, `{ from, to }` naming each side's column, or (over one router) the router's own relation function.

DashboardProblem

Something the dashboard refused or reported, by catalogued id.

PropertyTypeDescription
idstringThe catalogued warning id: `dashboard:panel:<id>`, `dashboard:link:<from>-><to>`, `dashboard:spec:<path>`, …
pathstringWhere in the spec, e.g. `panels[2].kind`.
messagestringWhat happened.

DashboardOptions

The third argument of {@link createDashboard}: the factories the dashboard builds with, and the AI callback. A factory not given here is looked up on its script-tag global (`LatticeGrid`, `LatticeGridLayout`, `LatticeGridKPI`, `LatticeGridLeaflet`, `LatticeGridDeck`); a panel whose factory is on neither is refused by name.

MethodSignatureParametersReturnsDescription
createGrid(el: HTMLElement, config: Record<string, unknown>) => Gridel: HTMLElement
config: Record<string, unknown>
=> GridThe grid's `createGrid`, for `grid` panels. (optional)
createHeadlessGrid(config: Record<string, unknown>) => Gridconfig: Record<string, unknown>=> GridThe grid's `createHeadlessGrid`, which holds a chart's, KPI panel's or map's source. (optional)
createPushdownSource(config: Record<string, unknown>) => unknownconfig: Record<string, unknown>=> unknownThe grid's `createPushdownSource`, for `pushdown` sources. (optional)
createChart(spec: Record<string, unknown>) => unknownspec: Record<string, unknown>=> unknownThe charts module's `createChart`, for `chart` panels and map charts. (optional)
createLayout(el: HTMLElement, config: Record<string, unknown>) => unknownel: HTMLElement
config: Record<string, unknown>
=> unknownThe layout module's `createLayout`. Without it nothing is built. (optional)
createKPI(el: HTMLElement, config: Record<string, unknown>) => unknownel: HTMLElement
config: Record<string, unknown>
=> unknownThe KPI module's `createKPI`, for `kpi` panels. (optional)
bindLeaflet(grid: Grid, options: Record<string, unknown>) => unknowngrid: Grid
options: Record<string, unknown>
=> unknownThe Leaflet module's `bindLeaflet`, for `map` panels with `binding: 'leaflet'`. (optional)
bindDeck(grid: Grid, options: Record<string, unknown>) => unknowngrid: Grid
options: Record<string, unknown>
=> unknownThe deck.gl module's `bindDeck`, for `map` panels with `binding: 'deckgl'`. (optional)
ai((payload: Record<string, unknown>) => Promise<unknown>) | { ask: (payload: Record<string, unknown>) => Promise<unknown> }(payload: Record<string, unknown>) => Promise<unknown>| { ask: (payload: Record<string, unknown>) => Promise<unknown> }The AI callback `propose()` asks: the same `ask(payload)` `createAI` takes (or `{ ask }`). Without it `propose()` declines by name. (optional)

DashboardPanelHandle

One built panel.

PropertyTypeDescription
idstringThe panel id.
kindDashboardPanelKind 'grid' | 'chart' | 'kpi' | 'map' | 'html'Its kind.
elHTMLElementThe element its viewer was mounted in, inside its window.
gridGrid | nullThe grid it reads (a grid panel's own), or null.
viewerunknownWhat its factory returned: the grid, chart, KPI panel or binding; null for html.

DashboardProposal

What {@link Dashboard.propose} resolves to.

PropertyTypeDescription
specDashboardSpec | nullThe checked spec to accept with `apply(spec)`, or null when nothing usable came back.
warningsstring[]Everything dropped or refused, one line each.

DashboardView

A saved dashboard view: the spec and each grid panel's state.

PropertyTypeDescription
idstringThe view's id.
namestringIts name.
specDashboardSpecThe dashboard spec, placements included.
gridsRecord<string, GridState>Each grid panel's `grid.state.get()`, by panel id.

Dashboard

A built dashboard.

Properties
PropertyTypeDescription
elHTMLElementThe element it is mounted on. (read-only)
layoutunknownThe layout the panels sit in, or null when nothing was built. (read-only)
presentingstring | nullThe panel a presentation shows, or null. (read-only)
Methods
MethodSignatureParametersReturnsDescription
spec(): DashboardSpec - DashboardSpecThe dashboard as built - its panels, the sources by id, the links that were made and each window's live placement - as a spec that rebuilds it.
apply(spec: DashboardSpec): DashboardProblem[]spec: DashboardSpecDashboardProblem[]Tear down and build this spec in its place: how a host accepts a proposal or restores a stored spec. Returns what was refused.
panels(): string[] - string[]The built panels' ids, in spec order.
panel(id: string): DashboardPanelHandle | nullid: stringDashboardPanelHandle | nullOne built panel, or null.
problems(): DashboardProblem[] - DashboardProblem[]Everything the last build refused or reported.
propose(prompt: string, request: { source: string; rows?: boolean; maxRows?: number; signal?: AbortSignal }): Promise<DashboardProposal>prompt: string
request: { source: string; rows?: boolean; maxRows?: number; signal?: AbortSignal }
Promise<DashboardProposal>Ask the AI callback for a spec over one source's columns. The model sees the column names and types and the panel kinds this page can draw - rows only with `rows: true` (at most `maxRows`, default 50). A panel naming a column the source lacks, a kind the page cannot draw or another source is dropped with a warning. Nothing is built: accept with `apply(spec)`. Without an AI callback it declines by name.
saveView(name: string): DashboardView | nullname: stringDashboardView | nullSave the spec and every grid panel's state as one named view; a name already saved is replaced.
views(): DashboardView[] - DashboardView[]The saved views, in the order first saved.
applyView(view: string | DashboardView): booleanview: string | DashboardViewbooleanRestore a view by id, or one the host stored: the layout and every grid's state.
present(options?: { panels?: string[]; from?: number }): booleanoptions?: { panels?: string[]; from?: number }booleanPresent the panels one at a time, each filling the dashboard (the layout's maximise).
step(by?: number): numberby?: numbernumberMove the presentation by this many panels, clamped; the position shown, or -1.
stopPresenting(): boolean - booleanEnd the presentation and put the dashboard back.
destroy(): void - voidTear everything down: links, viewers, grids, router attachments and the layout.
Events

No events.