# DashboardHandle

Import it from `@grafloria/element`.

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

```ts
interface DashboardHandle
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `views` | `string[]` |  | The view ids, in declaration order. |
| `activeView` | `string` |  | The 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()`:
- `exportIds(viewId?: string): Set<string>` — The node ids ONE view occupies — pass straight to `includeIds` to export just that board:
- `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`
