Skip to content
D
Documentation

DashboardHandle

reference
5 min readUpdated

Import it from @grafloria/element.

The typed façade — the erTable/umlClass equivalent for dashboards.

ts
interface DashboardHandle

Properties

NameTypeDefaultDescription
viewsstring[]The view ids, in declaration order.
activeViewstringThe currently shown view id.

Members

  • showView(id: string): void — Show a view (the others park off-camera) and frame it.
  • widget(id: string): WidgetHandle | undefined — A widget handle by id (undefined when unknown).
  • focusWidget(id: string): boolean — SELECT a widget and move keyboard focus to it — what a press on the widget does. The selected widget shows its painted grip (dragHandle: { grip }) and a quiet ring; a void click clears. False when the id is unknown.
  • selectWidget(id: string | undefined): boolean — SELECT a widget WITHOUT moving keyboard focus — the ring and the grip, nothing else; what a mouse press does. undefined clears the selection on every view. False when the id is unknown.
  • getSelectedWidget(): string | undefined — The selected widget, if any (across views: only the on-camera one can be).
  • widgetsOf(viewId?: string): WidgetHandle[] — Every widget handle of a view (default: the active one).
  • setLayout(layout: 'grid' | 'split', viewId?: string): void — Switch a view (default: the active one) between the cell grid and the split tree, live and keeping the picture: cells → tree by guillotine cuts, tree → cells by snapping to the columns. Persisted on the board, so a saved document reopens in the layout it was left in.
  • getLayout(viewId?: string): 'grid' | 'split' | 'tabs' — A container built with layout: 'tabs' reports 'tabs'; views never do.
  • setCaption(id: string, caption: SectionCaption): boolean — Set a SECTION's caption live — false removes it, true is the title, a string or the options. Repaints the band, gives the reserve back or takes it, persists on the section, one undo step. False for anything that is not a container.
  • getCaption(id: string): SectionCaption | undefined — The caption as authored or last set; undefined when the section has none.
  • activateTab(containerId: string, pageId: string): boolean — Show a PAGE of a tab container (layout: 'tabs'). False when the container or the page is not one. Persisted, so a saved board reopens on the page it was left on.
  • getActiveTab(containerId: string): string | undefined — The page showing in a tab container.
  • moveToTab(widgetId: string, containerId: string, index?: number): Promise<boolean> — Move a WIDGET into containerId as a NEW TAB at index (default: the end): the widget becomes the only member of a fresh page named by its title, and that page the active tab — what dropping a widget on a strip does. One undoable step; the board it left re-packs, and a page it emptied closes.
  • moveTab(containerId: string, pageId: string, index: number): boolean — Reorder a page along its container's strip. One undoable step; the order is saved with the container.
  • setSizing(mode: 'fit' | 'grow'): void — Live sizing/float switches — the two prototype toggles.
  • getSizing(): 'fit' | 'grow'
  • setFloat(on: boolean): void
  • getFloat(): boolean
  • setColumns(n: number, layout?: GridColumnLayout, viewId?: string): void — Set the COLUMN COUNT of every board (or one view), live. Goes through the engine's per-column layout cache, so shrinking then growing back restores the wide layout rather than re-deriving it. An explicit call PINS the count — the width-driven responsive evaluator stops overriding it.
  • getColumns(viewId?: string): number — The LIVE column count of a view (default: the active one).
  • setRtl(on: boolean): void — RTL mirroring, live — pixels only, cells never change.
  • getRtl(): boolean
  • setStatic(on: boolean): void — Static (read-only for the pointer) mode, live — the viewer/designer switch.
  • getStatic(): boolean
  • setDragHandle(v: DragHandleOption): void — Drag-handle mode, live, every view: true = the caption strip, a selector = your own handle, { grip: true, … } = a painted grip, false = the whole card.
  • getDragHandle(): DragHandleOption
  • addWidget(spec: DashboardWidgetSpec, viewId?: string, opts?: { displaced?: Command[] }): WidgetHandle | undefined — Add a widget to a view. CREATES the node (you do not pre-build one), wires its metadata, and commits node + membership as ONE undoable step. Auto-positions when the spec names no cell. opts.displaced: the commands a palette drop handed onDropIn for the tiles the placeholder pushed aside — folded into the same step, so the widget lands on the cell the drop showed and undo puts the pushed tiles back with it. Left out, the board is re-read from the model and the push is forgotten: the new widget then auto-positions into whatever hole is left (Quantia's "lands on the cell it was aimed at", element 0.4.54).
  • refresh(): void — Re-read every board from the model — call after undo/redo, or any out-of-band mutation, so the grid and the projection agree again.
  • fit(viewId?: string): void — Re-frame the camera on a view (default: the active one).
  • metrics(viewId?: string): ReturnType<DashboardGridHandle['metrics']> | undefined — Live geometry of a view's board (columns, gap, rows, rowHeight, frame…).
  • toJSON(): DashboardSnapshot — The whole board as plain data — feed it straight back to dashboard():
ts
dashboard({ ...handle.toJSON(), renderWidget });   // a true round trip

Everything DashboardOptions takes EXCEPT the function seams (renderWidget, onLayoutChange), which cannot be written to a file and must be supplied again on the way back in.

Values are read from the LIVE board, not from the authored literal, so a mode or column count the user changed after mount is what you get back.

This used to return only views, which made the round-trip claim true of the layout and false of the board: a board authored grow at a 10-column, 6px-gap geometry reloaded as a 12-column fit one. It is also what JSON.stringify(handle) calls, so the partial answer was a permanent footgun in a save API rather than merely an omission.

  • exportIds(viewId?: string): Set<string> — The node ids ONE view occupies — pass straight to includeIds to export just that board:
ts
api.export('pdf', { includeIds: handle.exportIds() });

WHY THIS EXISTS. Tabs park the inactive views far off-camera, which is invisible on screen and ruinous on export: export() frames the whole MODEL, so a two-view board writes a ~21,000px document that is almost entirely empty — with no warning, because nothing is technically wrong. Scoping was always possible; knowing WHAT to scope to was not.

The set includes the view's GROUP as well as its widgets. Rolling this by hand from toJSON() looks equivalent and is not — it drops the group, and the widgets export without the frame they sit in.

  • binderOf(viewId?: string): DashboardGridHandle | undefined — THE DOCUMENTED ESCAPE HATCH: the view's own bindDashboardGrid handle (default: the active view). Reach for it only for what this façade does not cover yet — palette drag-in (beginPaletteDrag), board metrics(), cellRectOf, planRemoval, and re-sync() after an external undo. Every call site is a named gap in this API, not a normal way to drive a board.
  • dispose(): void

Was this page helpful?