api reference
Shell API Reference
createShell: an application layout of nested rows and columns of panels, with splitters, collapse to a header strip, tabs, and content that keeps its state through hide, collapse and tab switches.
API reference › The application shell
All 25 pages Everything on one page → Developer guide →
The shell module
modules/shell is an opt-in layout container: a tree of cells nested as rows and columns to any depth, sized by pure CSS flexbox, in its own bundle that grows grid core by nothing. It replaces DHX Layout in DemandFlow and is a sold Lattice feature. The main owner pain it removes is asynchronous redraw: when createShell(el, config) returns, every cell element is already in the document and already sized - a fixed size is a flex-basis, gravity is a flex-grow, and min/max are the CSS min-*/max-* properties - so there is no awaitRedraw(), no deferred JS paint and no requestAnimationFrame to wait on. A third-party widget mounted into getCell(id).el on the very next line measures a non-zero size in the same synchronous task, and a window resize reflows by CSS alone, with no listener or timer in the module.
import { createShell } from '@toclocoinc/lattice-grid/modules/shell';
const shell = createShell(document.querySelector('#shell'), {
type: 'line', // separators: 'line' | 'wide' | 'space' | 'none'
rows: [
{ id: 'nav', width: 220 }, // fixed px, never grows
{ id: 'main', gravity: 1, // takes the remaining space
cols: [
{ id: 'header', height: 40 },
{ id: 'body', gravity: 1 },
] },
],
});
shell.getCell('body').el.appendChild(widget); // already sized
shell.destroy();
One element per cell. A cell is the flex item of its parent and, when it declares nested rows or cols, the flex container of its children - getCell(id).el is that single element, so a leaf cell is a plain block a widget fills and a nested cell already lays its own children out. Every size is an inline style, never a class, so the geometry a test reads is the geometry a host gets: width/height take a pixel number or any CSS length string ('30%', '12rem', 'calc(…)'); a cell with a fixed size never grows, and the gravity of the cells without one shares the space that is left after the fixed sizes are subtracted, in proportion. minWidth/maxWidth/minHeight/maxHeight clamp a cell exactly as the CSS properties do, padding pads the cell (its widget mounts inside the padding), align is the cell's align-self, and css is an object of camelCase properties or a string of declarations written straight onto the element - never parsed as HTML.
Headers and collapse. A cell may carry a header - text, a custom element to mount, or an image via headerImage/headerIcon - drawn as a strip headerHeight tall (default 32) above the content, with the cell's padding pushed down into the content area so the header spans the cell edge-to-edge. A collapsable cell gets a collapse control in that strip and collapse()/expand()/toggle() on its handle; collapsed: true starts it folded. Folding writes one thing - the cell's own flex-basis becomes the strip height - so the neighbours take the space and expand() restores the exact previous size, with the content element back in flow and measurable on the very next line: the transition is one synchronous pass of inline styles, no animation and no deferred paint. A folded strip among rows runs its text vertically (writing-mode: vertical-rl) so a narrow strip still reads. The change is gated and reported: cancellable beforeCollapse/beforeExpand (a handler calls preventDefault(reason?) or returns false) precede the move, and afterCollapse/afterExpand follow it, all subscribed through shell.on(name, fn). The control's accessible label is translated through config.messages, which the module resolves with its own English fallback.
Separators are the shell's type, not markup. 'line' draws a thin border between cells, 'wide' a thick one, 'space' an even gap, and 'none' (the default) nothing - each reads a --lattice-* theme token with a literal fallback, so a shell inside a themed grid inherits the theme and one on a bare page still draws, dark mode included. The interactive pieces the module adds are the collapse control on a collapsable cell - a native <button>, so Enter and Space toggle it and its aria-expanded/aria-label announce the state - and the splitters below; destroy() empties the element, removes the class the module added, restores the host's own inline style and drops every cell record and splitter listener, and the host still owns the element.
State round-trips the whole arrangement, content untouched. getState() snapshots the separator, the root padding/css and every cell's sizes, gravity, min/max and mutable collapsed/hidden/selected flags as one JSON-able object. setState(state) re-resolves and re-mounts it through the very same path a fresh config takes, then moves the exact DOM nodes a host mounted into each cell back into the matching cell by id - re-parented, never cloned - and fires stateChange with the state now live; on(name, fn)/off(name, fn) subscribe to it. The same snapshot is what the htmx data-lattice-shell history save/restore carries, and what the React, Vue, Svelte and Angular shell components re-emit, so a save/restore round-trip reproduces the layout exactly while a host's widgets survive it.
A cell marked resizable: true gets a splitter, not a separator. The splitter sits on the cell's trailing border and resizes that cell and its next sibling together, within both cells' minWidth/maxWidth (or minHeight/maxHeight on a column axis). It is a focusable role="separator" with aria-valuenow/aria-valuemin/aria-valuemax: drag it (mouse or touch) or focus it and use the arrow keys to step, Home/End to jump to the range, and double-click to restore the configured sizes. Every gesture runs through the shell's events - beforeResizeStart (cancellable: call event.preventDefault(reason) or return false to keep the sizes), then resize as it moves, then afterResizeEnd when it stops, each carrying the two cells' ids and their sizes - observed with on(name, fn) and detached with off(name, fn).
Content that never dies. cell.attach(content) takes a Lattice view (a grid, Gantt, scheduler, chart or layout), a DOM element or an HTML string (converted once), and the cell keeps the same element for its whole life: hide/show, collapse/expand, tab switches, resizes and moving the cell never re-render, innerHTML or destroy it, so a third-party widget's state and listeners persist. cell.getWidget() reads it, cell.detach() hands it back intact, and only detach() or destroy() releases it. Optional onHidden/onVisible hooks - in attach's options or on a tab - fire when the content hides and shows, and an attached Lattice view is re-measured on becoming visible through its own resize/refresh method, never a DHX-era paint(). A cell may instead declare tabs: [{ id, label, content? }] to render a keyboard-operable tab strip (ARIA tabs, arrow keys) with one attached content per tab, each preserved across switches; cell.getTab(id) reaches a tab and shell.on('tabChange', …) fires { cellId, tabId, previousTabId } on every switch.
Live structure. The tree is not frozen at creation. cell.hide() sets the cell's flex item to display:none, so its space is handed to its neighbours by CSS reflow alone; cell.show() restores the exact display the cell was built with, and cell.isVisible() reads the current state - a cell may also start hidden with hidden: true. shell.addCell(parentId, config, index?) builds a new cell through the view's one synchronous pass, so its element is already in the document and sized when the call returns (mountable on the very next line), and shell.removeCell(id) removes a cell and its whole subtree live, releasing every piece of content inside it (detached, never destroyed) and handing the space back. cell.progressShow() overlays an indeterminate spinner on one cell without touching its size or content, cell.progressHide() clears it, and a cell declared progressDefault: true starts with the overlay on. Every mutation is gated by a cancellable before* event and announced by an after* event - beforeHide/afterHide, beforeShow/afterShow, beforeAddCell/afterAddCell, beforeRemoveCell/afterRemoveCell - and a handler that calls event.preventDefault(reason) or returns false vetoes the change, which then happens not at all.
| Member | Description |
|---|---|
| createShell(el, config) | Create a shell. config is { rows | cols, type, padding, css, messages }; rows lays the top-level cells left-to-right, cols top-to-bottom (used when rows is absent), and messages supplies a t(key, params) catalogue for the collapse control's labels. |
| cell | Each cell: id, width/height (px or %), gravity (default 1), minWidth/maxWidth/minHeight/maxHeight, padding, css, align, a header (text, headerIcon, headerImage or a custom element) with headerHeight, collapsable/collapsed, hidden, selected, progressDefault, resizable (a splitter on the trailing border), nested rows/cols, or tabs for tabbed content. |
| getCell(id) | The cell handle { el, config, collapsed, getParent(), collapse(), expand(), toggle(), attach, detach, getWidget, getTab, hide, show, isVisible, progressShow, progressHide } - el is the content element, config the normalised cell, getParent() the parent handle or null at the root. undefined for an unknown id. |
| attach(content, opts?) | Mount a Lattice view, a DOM element or an HTML string; the cell keeps the same element for its life. On a tabbed cell this attaches to the active tab. opts is { onHidden, onVisible }. |
| detach() | Remove the cell's (or active tab's) content and hand it back intact. Returns null when nothing is attached. |
| getWidget() | The attached content - a view, element or the converted element of a string - or null. |
| getTab(id) | One tab of a tabbed cell: { id, label, el, active(), activate(), attach(), detach(), getWidget() }. undefined on a non-tabbed cell or an unknown id. |
| getState() | The arrangement as one JSON-able snapshot: { version, type, padding, css, rows | cols }, every cell carrying its sizes and mutable state. Exactly what setState accepts. |
| setState(state) | Apply a snapshot: re-resolve and re-mount the cells, move the host's mounted content back by id (the same nodes, never cloned), and fire stateChange. |
| hide() / show() / isVisible() | Hide the cell (display:none, so its space reflows to its neighbours) or show it again, restoring its exact display; isVisible() reads the state. A cell declared hidden: true starts hidden. beforeHide/beforeShow gate each, afterHide/afterShow announce it. |
| addCell(parentId, config, index?) | Add a cell to the tree live and synchronously - in the document and sized when it returns. parentId is the parent cell id (or null for the root); a leaf or tabbed cell is refused. index places it among the parent's children (the end when omitted). |
| removeCell(id) | Remove a cell and its whole subtree live, releasing every content inside it (detached, never destroyed) and returning the removed handle. |
| progressShow() / progressHide() | Overlay an indeterminate spinner on the cell - or clear it - without touching its size or content. progressDefault: true starts a cell with the overlay on. |
| forEach(fn) | Walk every cell in tree (document) order, handing each handle to fn. |
| on(name, fn) | Subscribe to a shell event - beforeCollapse/afterCollapse, beforeExpand/afterExpand, beforeAddCell/afterAddCell, beforeRemoveCell/afterRemoveCell, beforeHide/afterHide, beforeShow/afterShow, tabChange, stateChange, or a resize event - beforeResizeStart (cancellable), resize, afterResizeEnd - or to '*' for every past-tense event; returns an unsubscribe. A before* handler cancels by calling preventDefault(reason?) or returning false. |
| off(name, fn) | Remove a handler registered with on. |
| destroy() | Release every content, empty the element, remove only what the module added, and drop every cell record, splitter listener and handler. The host still owns the element and every content element again. |
DHX Layout migration
DemandFlow moves from dhtmlx Layout to modules/shell. The arrangement is the same shape - nested rows/cols of named cells - but the shell is synchronous, token-themed and state-serialisable where DHX Layout is an imperative widget API.
| DHX Layout | Lattice shell |
|---|---|
| new dhx.Layout(el, { rows: [...] }) | createShell(el, { rows: [...] }) |
| { id, header } | { id } - headers are content a host mounts into getCell(id).el, so there is no header bar to theme or chase. |
| { width, height, gravity } | The same keys: width/height are pixels or any CSS length, gravity shares the space left after the fixed sizes. |
| { minWidth, maxWidth, minHeight, maxHeight, padding } | The same keys, written as the CSS min-*/max-*/padding properties. |
| { collapsable, resizable } | collapsed/hidden/selected - mutable per-cell state, round-tripped by getState()/setState(). |
| cell.attach(widget) / attachHTML(html) | shell.getCell(id).el.appendChild(widget) - a plain element, sized synchronously. |
| cell.collapse() / expand() / toggle(), hide() / show() | shell.setState({ ...shell.getState(), rows: [...] }) with the flags changed; content is restored by id. |
| layout.destructor() | shell.destroy() - empties the element and drops every record; the host still owns the element. |
Three levels, state round-trip, and teardown, executed
A row of a fixed nav and a gravity main, the main split into a fixed header and two gravity
columns, walked, snapshot-and-restored through getState()/setState(), and torn down over
the in-tree DOM test double - the sizes themselves are asserted against real Chrome in
test/shell-browser.test.js.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createShell } = await import('../packages/modules/shell/index.js');
const { root, document: doc } = createTestDom();
const shell = createShell(root, {
type: 'line', padding: 4, css: { position: 'relative' },
rows: [
{ id: 'nav', width: 200 },
{ id: 'main', gravity: 1, cols: [
{ id: 'top', height: 40, padding: 8 },
{ id: 'body', gravity: 2, minWidth: 50, maxWidth: 900 },
{ id: 'foot', gravity: 1 },
] },
],
});
const tag = shell.getCell('nav').el.tagName; // DIV - the content element itself
const parent = shell.getCell('body').getParent().config.id; // 'main' - the cell that nests it
let walked = 0;
shell.forEach(() => { walked += 1; }); // nav, main, top, body, foot
const unknown = shell.getCell('nope') === undefined; // true - no such cell
// State round-trips the whole arrangement, content untouched.
const widget = doc.createElement('div');
widget.textContent = 'kept';
shell.getCell('body').el.appendChild(widget);
let events = 0;
const onStateChange = () => { events += 1; };
shell.on('stateChange', onStateChange);
shell.setState(shell.getState()); // re-mounts and fires stateChange
shell.off('stateChange', onStateChange); // off - the subscription is removed
const kept = shell.getCell('body').el.contains(widget); // true - the same node, re-parented
const widthAfter = shell.getState().rows[0].width; // 200 - sizes survived
shell.destroy();
return [tag, parent, walked, unknown, root.children.length, kept, events, widthAfter].join('|');
A resizable cell, its splitter and the resize events, executed
A cell marked resizable: true puts a focusable role="separator" on its
trailing border; dragging or arrow keys resize it and its next sibling within both cells' min/max, and
on(name, fn) observes the gesture. The drag itself is asserted against real Chrome in
test/shell-splitter-browser.test.js; this block asserts the splitter's DOM and the event surface over
the test DOM.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createShell } = await import('../packages/modules/shell/index.js');
const { root } = createTestDom();
const shell = createShell(root, { type: 'line', rows: [
{ id: 'a', width: 200, minWidth: 100, maxWidth: 300, resizable: true },
{ id: 'b', gravity: 1 },
] });
const splitter = root.querySelector('[role="separator"]');
const oriented = splitter.getAttribute('aria-orientation'); // 'vertical' - a row resizes widths
const labelled = splitter.getAttribute('aria-label'); // 'Resize'
const tabbable = splitter.tabIndex === 0; // true - keyboard reachable
let resized = 0;
const off = shell.on('resize', () => { resized += 1; }); // subscribe; returns an unsubscribe fn
off(); // remove the handler
shell.on('nope', () => { resized += 1; }); // unknown name: never subscribed
shell.destroy(); // removes the splitter too
return [oriented, labelled, tabbable, resized, typeof shell.off].join('|');
Headers, collapse, a vetoed fold and translated labels, executed
A column cell with a text header folds to its strip, a beforeCollapse handler vetoes the first fold, expand() restores it, and config.messages supplies the control's own labels - all synchronous, over the in-tree DOM test double; the real rects are asserted against Chrome in test/shell-browser.test.js.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createShell } = await import('../packages/modules/shell/index.js');
const { root } = createTestDom();
const shell = createShell(root, {
messages: { t: (key, params) => (key === 'shell.collapse' ? `Hide ${params.name}` : `Show ${params.name}`) },
cols: [
{ id: 'top', height: 120, header: 'Top', collapsable: true },
{ id: 'bottom', gravity: 1 },
],
});
const top = shell.getCell('top');
const btn = top.el.parentNode.querySelector('.lat-shell__header-btn');
const label = btn.getAttribute('aria-label'); // 'Hide Top' - the catalogue reached the control
const start = top.collapsed; // false - open at mount
const refuse = (e) => e.preventDefault('not yet');
shell.on('beforeCollapse', refuse);
const vetoed = top.collapse(); // false - the host refused the fold
shell.off('beforeCollapse', refuse);
const folded = top.collapse(); // true - folds to the strip
const after = top.collapsed; // true
const restored = top.expand(); // true - back to the exact size
shell.destroy();
return [label, start, vetoed, folded, after, restored].join('|');
Attach, tab switching and detach, executed
A tabbed cell holds one content per tab; switching hides the outgoing panel without touching its
content, tabChange fires, and detach() hands the element back - run over the in-tree DOM test
double, with the same-element identity and a real grid's re-measure asserted against real Chrome in
test/shell-content-browser.test.js.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createShell } = await import('../packages/modules/shell/index.js');
const { root } = createTestDom();
const shell = createShell(root, {
rows: [{ id: 'main', tabs: [
{ id: 'a', label: 'A', content: '<b>one</b>' },
{ id: 'b', label: 'B' },
] }],
});
const cell = shell.getCell('main');
const widget = root.ownerDocument.createElement('div');
cell.getTab('b').attach(widget); // b was empty - attach straight to it
let last = '';
shell.on('tabChange', (e) => { last = e.tabId; });
cell.getTab('b').activate(); // switch: a hides, b shows
const switched = last === 'b'; // tabChange fired
const kept = cell.getTab('a').getWidget().textContent === '<b>one</b>'; // a's content survived the switch
const same = cell.getWidget() === widget; // the same element, not a clone
const back = cell.detach(); // detach b's content intact
shell.destroy();
return [switched, kept, same, back === widget].join('|');
Hide, show, add, remove, progress and a vetoed add, executed
A cell hides and shows, a new cell is added and removed live, the progress overlay mounts and
clears, and a beforeAddCell handler vetoes one add so nothing is created - run over the in-tree DOM test
double, with the real flexbox geometry (the space a hide gives and a show restores, and the size of a synchronously
added cell) asserted against real Chrome in test/shell-live-browser.test.js.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createShell } = await import('../packages/modules/shell/index.js');
const { root } = createTestDom();
const shell = createShell(root, { rows: [
{ id: 'a', width: 200 },
{ id: 'b', gravity: 1, progressDefault: true }, // starts with the spinner on
] });
const a = shell.getCell('a');
const hidden = a.hide().isVisible() === false; // display:none, the space reflows
const shown = a.show().isVisible() === true; // show restores the exact display
shell.on('beforeAddCell', (e) => { if (e.id === 'no') e.preventDefault(); });
const added = shell.addCell(null, { id: 'c', width: 100 }); // sized synchronously
const vetoed = shell.addCell(null, { id: 'no' }) === undefined; // vetoed: nothing made
const busy = shell.getCell('b').el.getAttribute('aria-busy') === 'true';
shell.getCell('c').progressShow(); // overlay the new cell
const overlay = shell.getCell('c').el.children.length === 1;
shell.getCell('c').progressHide();
const cleared = shell.getCell('c').el.children.length === 0;
const removed = shell.removeCell('c').config.id; // live remove, content released
const gone = shell.getCell('c') === undefined;
shell.destroy();
return [hidden, shown, !!added, vetoed, busy, overlay, cleared, removed, gone].join('|');
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.
The shell module
ShellWidget
A Lattice view as `attach()` sees it: an object exposing its root element and, optionally, a resize/refresh method the shell nudges on show.
| Property | Type | Description |
|---|---|---|
| element | HTMLElement | The view's root element (`createGrid` exposes `.element`). (optional) |
| el | HTMLElement | The view's root element (the scheduler and layout expose `.el`). (optional) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| resize | (): void | - | void | Re-measure after a container change; preferred over `refresh` when both exist. (optional) |
| refresh | (): void | - | void | Re-measure after a container change (the layout's method). Never a `paint()`. (optional) |
ShellAttachHooks
The optional hooks `attach(content, opts)` takes, fired when the content hides or shows.
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| onHidden | (content: ShellContent): void | content: ShellContent | void | Called when the content hides (a tab switch away, a collapse, a hide). (optional) |
| onVisible | (content: ShellContent): void | content: ShellContent | void | Called when the content becomes visible again. (optional) |
ShellTab
One tab of a tabbed cell: its identity, its strip label, and the content it holds across switches.
| Property | Type | Description |
|---|---|---|
| id | string | The tab's identity for `getTab(id)`. |
| label | string | The text on the tab. Defaults to `id`. (optional) |
| content | ShellContent | The content the tab holds, attached the same way `attach()` attaches it. (optional) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| onHidden | (content: ShellContent): void | content: ShellContent | void | Called when this tab's content hides on a switch away. (optional) |
| onVisible | (content: ShellContent): void | content: ShellContent | void | Called when this tab's content becomes visible on a switch to it. (optional) |
ShellCell
One cell of a shell. A cell either mounts content or nests `rows`/`cols`.
| Property | Type | Description |
|---|---|---|
| id | string | The cell's identity for `getCell(id)`. Auto-generated when omitted. (optional) |
| width | ShellSize number | string | The cell's width along a row (px or %). (optional) |
| height | ShellSize number | string | The cell's height along a column (px or %). (optional) |
| gravity | number | Share of the spare space when the cell has no fixed size. Default 1. (optional) |
| minWidth | ShellSize number | string | The cell's minimum width. (optional) |
| maxWidth | ShellSize number | string | The cell's maximum width. (optional) |
| minHeight | ShellSize number | string | The cell's minimum height. (optional) |
| maxHeight | ShellSize number | string | The cell's maximum height. (optional) |
| resizable | boolean | When true, a splitter on the trailing border resizes this cell and its next sibling. (optional) |
| padding | number | string | The cell's inner padding: a CSS padding value. (optional) |
| css | Record<string, string | number> | string | Extra CSS: an object of camelCase properties, or a string of declarations. (optional) |
| align | string | The cell's `align-self` across the parent's axis. (optional) |
| header | string | Node | The header strip's content: text, or a custom element to mount. (optional) |
| headerIcon | string | An icon in the header strip: a URL (rendered as an image) or a glyph/emoji. (optional) |
| headerImage | string | An image in the header strip, rendered as an `<img>` with this `src`. (optional) |
| headerHeight | ShellSize number | string | The header strip's height on the cell's main axis (px or a CSS length). Default 32. (optional) |
| collapsable | boolean | Show a collapse control in the header and let `collapse()`/`expand()` fold the cell. (optional) |
| collapsed | boolean | Start the cell folded to its header strip (implies `collapsable`); also the cell's current collapsed state. (optional) |
| hidden | boolean | Mutable state: whether the cell is hidden from the layout; `hidden: true` also starts it hidden until `show()`. (optional) |
| selected | string | Mutable state: the id of the selected tab inside the cell, when it hosts tabs. (optional) |
| rows | ShellCell[] | Nested cells, laid out left-to-right. (optional) |
| cols | ShellCell[] | Nested cells, laid out top-to-bottom. (optional) |
| tabs | ShellTab[] | Tabbed content: a strip with one attached content per tab, each preserved across switches. (optional) |
| progressDefault | boolean | Start the cell with its loading indicator already overlaid. Default false. (optional) |
ShellConfig
The configuration for {@link createShell}.
| Property | Type | Description |
|---|---|---|
| rows | ShellCell[] | The top-level cells, laid out left-to-right. (optional) |
| cols | ShellCell[] | The top-level cells, laid out top-to-bottom (used when `rows` is absent). (optional) |
| type | ShellType 'line' | 'wide' | 'space' | 'none' | The separator between cells. Default `'none'`. (optional) |
| padding | number | string | The shell root's inner padding: a CSS padding value. (optional) |
| css | Record<string, string | number> | string | Extra CSS on the shell root. (optional) |
| messages | { t(key: string, params?: Record<string, string | number>): string } | A message catalogue with a `t(key, params)` formatter, for the collapse control's labels. (optional) |
ShellCellInstance
The cell handle `getCell(id)` and `forEach` hand back.
| Property | Type | Description |
|---|---|---|
| el | HTMLElement | The cell's content element - mount a widget into it directly. (read-only) |
| config | ShellCell | The cell's normalised config. (read-only) |
| collapsed | boolean | Whether the cell is currently folded to its header strip. (read-only) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| getParent | (): ShellCellInstance | null | - | ShellCellInstance | null | The parent cell's handle, or `null` for a top-level cell. |
| collapse | (): boolean | Promise<boolean> | - | boolean | Promise<boolean> | Fold the cell to its header strip. No-op on a non-collapsable cell. |
| expand | (): boolean | Promise<boolean> | - | boolean | Promise<boolean> | Open the cell back to its exact previous size. No-op when already open. |
| toggle | (): boolean | Promise<boolean> | - | boolean | Promise<boolean> | Collapse the cell when open, expand it when folded. |
| attach | (content: ShellContent, opts?: ShellAttachHooks): ShellCellInstance | content: ShellContentopts?: ShellAttachHooks | ShellCellInstance | Attach a Lattice view, a DOM element or an HTML string to this cell. The cell keeps the same element for its life - hide/show, collapse/expand, tab switches, resizes and moves never re-render it - and only `detach()` or `destroy()` releases it. On a tabbed cell this attaches to the active tab. |
| detach | (): ShellContent | null | - | ShellContent | null | Detach this cell's content (the active tab's, when tabbed) and hand it back intact. |
| getWidget | (): ShellContent | null | - | ShellContent | null | The content this cell (or its active tab) holds, or `null`. |
| getTab | (id: string): ShellTabInstance | undefined | id: string | ShellTabInstance | undefined | The handle for one of a tabbed cell's tabs, or `undefined` for a non-tabbed cell or an unknown id. |
| isVisible | (): boolean | - | boolean | Whether the cell is currently visible (not hidden). |
| hide | (): ShellCellInstance | - | ShellCellInstance | Hide the cell, giving its space to its neighbours by CSS reflow. The cancellable `beforeHide` runs first; a veto leaves the cell as it was. Content stays mounted and its `onHidden` hook fires. |
| show | (): ShellCellInstance | - | ShellCellInstance | Show a hidden cell, restoring its space. The cancellable `beforeShow` runs first; a veto leaves the cell hidden. An attached Lattice view is nudged to re-measure and its `onVisible` hook fires. |
| progressShow | (): ShellCellInstance | - | ShellCellInstance | Overlay a loading indicator on the cell, without touching its size or content. |
| progressHide | (): ShellCellInstance | - | ShellCellInstance | Clear the cell's loading indicator. |
ShellTabInstance
The tab handle `getTab(id)` hands back for one tab of a tabbed cell.
| Property | Type | Description |
|---|---|---|
| id | string | The tab's id. (read-only) |
| label | string | The tab's strip label. (read-only) |
| el | HTMLElement | The tab's panel element - its content mounts here. (read-only) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| active | (): boolean | - | boolean | Whether this is the active tab. |
| activate | (): ShellTabInstance | - | ShellTabInstance | Make this tab active, firing `tabChange` and the content visibility hooks. |
| attach | (content: ShellContent, opts?: ShellAttachHooks): ShellTabInstance | content: ShellContentopts?: ShellAttachHooks | ShellTabInstance | Attach content to this tab, replacing what it held. |
| detach | (): ShellContent | null | - | ShellContent | null | Detach this tab's content and hand it back. |
| getWidget | (): ShellContent | null | - | ShellContent | null | This tab's content, or `null`. |
ShellEventPayloads
What a handler receives, per shell event.
| Property | Type | Description |
|---|---|---|
| beforeCollapse | ShellBeforeToggleEvent | The fold about to happen, with `preventDefault` to refuse it. |
| beforeExpand | ShellBeforeToggleEvent | The unfold about to happen, with `preventDefault` to refuse it. |
| afterCollapse | ShellAfterToggleEvent | The fold that happened. |
| afterExpand | ShellAfterToggleEvent | The unfold that happened. |
| beforeAddCell | ShellBeforeAddCellEvent | The cell about to be added, with `preventDefault` to refuse it. |
| afterAddCell | ShellAfterAddCellEvent | The cell that was just added. |
| beforeRemoveCell | ShellBeforeRemoveCellEvent | The cell about to be removed, with `preventDefault` to refuse it. |
| afterRemoveCell | ShellAfterRemoveCellEvent | The cell that was just removed. |
| beforeHide | ShellBeforeHideEvent | The cell about to be hidden, with `preventDefault` to refuse it. |
| afterHide | ShellAfterHideEvent | The cell that just hid. |
| beforeShow | ShellBeforeShowEvent | The cell about to be shown, with `preventDefault` to refuse it. |
| afterShow | ShellAfterShowEvent | The cell that just showed. |
| tabChange | ShellTabChangeEvent | Which tab became active, and which it replaced. |
| stateChange | ShellStateChangeEvent | The `stateChange` payload: the state now live, the shape `getState()` returns. |
| beforeResizeStart | ShellResizeStartEvent | The two cells about to resize, and their current sizes. |
| resize | ShellResizeEvent | The two cells, and their new sizes. |
| afterResizeEnd | ShellResizeEvent | The two cells, and their final sizes. |
ShellCellEvent
The fields every shell event carries.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: one of the `ShellEventName` names. |
| id | string | The id of the cell the event is about. |
| origin | ViewerOrigin 'api' | 'user' | Who asked for the change: `user` for the header control, `api` for `collapse()`/`expand()`/`toggle()`. |
ShellBeforeToggleEvent
A `beforeCollapse` or `beforeExpand` payload. A handler refuses the change by calling `preventDefault(reason?)` or returning `false` (or a Promise that resolves `false`); the cell then does not move.
| Property | Type | Description |
|---|---|---|
| defaultPrevented | boolean | True once a handler has refused the change. |
| reason | string | null | The reason given to `preventDefault`, or null while nothing has refused. |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Refuse the change; the optional reason is surfaced to the caller. |
ShellAfterToggleEvent
An `afterCollapse` or `afterExpand` payload - a notification, not a gate.
| Property | Type | Description |
|---|---|---|
| collapsed | boolean | The cell is now collapsed (`afterCollapse`) or expanded (`afterExpand`). |
ShellBeforeAddCellEvent
The payload of a `beforeAddCell` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `beforeAddCell`. |
| id | string | The id the new cell will have. |
| parentId | string | null | The id of the cell it will be added into, or `null` for the shell root. |
| index | number | The index the new cell will take among its siblings. |
| reason | string | null | The reason given to `preventDefault`, or `null` while nothing has refused the add. |
| defaultPrevented | boolean | True once a handler has refused the add. |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Refuse the add; the cell is then not created. |
ShellAfterAddCellEvent
The payload of an `afterAddCell` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `afterAddCell`. |
| id | string | The id of the cell that was added. |
| parentId | string | null | The id of the cell it was added into, or `null` for the shell root. |
| index | number | The index the cell took among its siblings. |
ShellBeforeRemoveCellEvent
The payload of a `beforeRemoveCell` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `beforeRemoveCell`. |
| id | string | The id of the cell about to be removed. |
| parentId | string | null | The id of the cell that owns it, or `null` for a top-level cell. |
| reason | string | null | The reason given to `preventDefault`, or `null` while nothing has refused the remove. |
| defaultPrevented | boolean | True once a handler has refused the remove. |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Refuse the remove; the cell then stays mounted. |
ShellAfterRemoveCellEvent
The payload of an `afterRemoveCell` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `afterRemoveCell`. |
| id | string | The id of the cell that was removed. |
| parentId | string | null | The id of the cell that owned it, or `null` for a top-level cell. |
ShellBeforeHideEvent
The payload of a `beforeHide` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `beforeHide`. |
| cellId | string | The id of the cell about to be hidden. |
| reason | string | null | The reason given to `preventDefault`, or `null` while nothing has refused the hide. |
| defaultPrevented | boolean | True once a handler has refused the hide. |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Refuse the hide; the cell then stays visible. |
ShellAfterHideEvent
The payload of an `afterHide` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `afterHide`. |
| cellId | string | The id of the cell that just hid. |
ShellBeforeShowEvent
The payload of a `beforeShow` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `beforeShow`. |
| cellId | string | The id of the cell about to be shown. |
| reason | string | null | The reason given to `preventDefault`, or `null` while nothing has refused the show. |
| defaultPrevented | boolean | True once a handler has refused the show. |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Refuse the show; the cell then stays hidden. |
ShellAfterShowEvent
The payload of an `afterShow` event.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `afterShow`. |
| cellId | string | The id of the cell that just showed. |
ShellTabChangeEvent
The payload of a `tabChange` event.
| Property | Type | Description |
|---|---|---|
| cellId | string | The id of the cell whose tab changed. |
| tabId | string | The id of the now-active tab. |
| previousTabId | string | null | The id of the previously-active tab, or `null` on the first switch. |
ShellState
The JSON-able snapshot `getState()` returns and `setState()` accepts.
| Property | Type | Description |
|---|---|---|
| version | number | The state-format version; currently `1`. |
| type | ShellType 'line' | 'wide' | 'space' | 'none' | The separator between cells. |
| padding | number | string | The shell root's inner padding. (optional) |
| css | Record<string, string | number> | string | Extra CSS on the shell root. (optional) |
| rows | ShellCell[] | The top-level cells laid out left-to-right, each carrying its sizes, gravity and mutable state. (optional) |
| cols | ShellCell[] | The top-level cells laid out top-to-bottom (used when `rows` is absent). (optional) |
ShellStateChangeEvent
`stateChange`: `setState()` applied a snapshot and the arrangement now matches it. A notification, so it carries no `preventDefault`.
| Property | Type | Description |
|---|---|---|
| type | string | Which event this is: `stateChange`. |
| state | ShellState | The state now live - the same shape `getState()` returns. |
ShellBeforeEvent
The members every cancellable shell before-event carries.
| Property | Type | Description |
|---|---|---|
| type | string | The event's own name. |
| origin | EventOrigin 'api' | 'user' | 'init' | 'ai' | Where the action came from. |
| defaultPrevented | boolean | True once any handler has cancelled the resize. (read-only) |
| reason | string | null | The first reason given to `preventDefault`, or null. (read-only) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Cancel the pending resize; the reason is surfaced on the event's `reason`. |
ShellResizeEvent
The two neighbouring cells a splitter resize affects, plus their sizes.
| Property | Type | Description |
|---|---|---|
| axis | 'rows' | 'cols' | The layout axis being resized: `'rows'` resizes widths, `'cols'` resizes heights. |
| cells | [string, string] | The two cells' ids, in axis order (the `resizable` cell first). |
| sizes | [number, number] | Each cell's main-axis size in pixels, in the same order as `cells`. |
Shell
The shell instance {@link createShell} returns.
Properties
| Property | Type | Description |
|---|---|---|
| el | HTMLElement | null | The host element. (read-only) |
| config | ShellConfig | The normalised config. (read-only) |
Methods
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| getCell | (id: string): ShellCellInstance | undefined | id: string | ShellCellInstance | undefined | The cell handle for an id, or `undefined` when no cell has that id. |
| forEach | (fn: (cell: ShellCellInstance) => void): void | fn: (cell: ShellCellInstance) => void | void | Walk every cell in tree order, handing each handle to `fn`. |
| getState | (): ShellState | - | ShellState | Snapshot the arrangement as JSON: sizes, gravity, and every cell's mutable state. |
| setState | (state: ShellState): void | state: ShellState | void | Apply a snapshot, preserving the content mounted into each cell; fires `stateChange`. |
| addCell | (parentId: string | null | undefined, config?: ShellCell, index?: number): ShellCellInstance | undefined | parentId: string | null | undefinedconfig?: ShellCellindex?: number | ShellCellInstance | undefined | Add a cell to the tree live, synchronously - its element is in the document and sized when this returns. `parentId` is the cell to add into (or `null`/`undefined` for the shell root; a leaf or tabbed cell is refused), and `index` is where among the parent's children (the end when omitted). The cancellable `beforeAddCell` runs first, then `afterAddCell`. |
| removeCell | (id: string): ShellCellInstance | undefined | id: string | ShellCellInstance | undefined | Remove a cell and its subtree from the tree live, releasing every piece of content inside it (detached, never destroyed). The cancellable `beforeRemoveCell` runs first, then `afterRemoveCell`. |
| on | (name: ShellEventName | '*', fn: (event: ShellEventPayloads[ShellEventName]) => void): () => void | name: ShellEventName | '*'fn: (event: ShellEventPayloads[ShellEventName]) => void | () => void | Subscribe to a shell event or to `'*'` for every event; returns an unsubscribe. |
| off | (name: ShellEventName | '*', fn: (event: ShellEventPayloads[ShellEventName]) => void): void | name: ShellEventName | '*'fn: (event: ShellEventPayloads[ShellEventName]) => void | void | Remove a handler registered with {@link Shell#on}. |
| destroy | (): void | - | void | Empty the element and drop every cell record, splitter listener and handler. |
Events
| Event | When | Payload | Cancellable |
|---|---|---|---|
| beforeCollapse | A cell is about to fold to its header strip; cancellable. | ShellBeforeToggleEvent | yes |
| beforeExpand | A cell is about to open back to its previous size; cancellable. | ShellBeforeToggleEvent | yes |
| afterCollapse | A cell folded to its header strip. | ShellAfterToggleEvent | no |
| afterExpand | A cell opened back to its previous size. | ShellAfterToggleEvent | no |
| beforeAddCell | A cell is about to be added; cancellable. | ShellBeforeAddCellEvent | yes |
| afterAddCell | A cell was added, already in the document and sized. | ShellAfterAddCellEvent | no |
| beforeRemoveCell | A cell is about to be removed; cancellable. | ShellBeforeRemoveCellEvent | yes |
| afterRemoveCell | A cell (and its subtree) was removed, its content released. | ShellAfterRemoveCellEvent | no |
| beforeHide | A cell is about to be hidden; cancellable. | ShellBeforeHideEvent | yes |
| afterHide | A cell hid, giving its space to its neighbours. | ShellAfterHideEvent | no |
| beforeShow | A cell is about to be shown; cancellable. | ShellBeforeShowEvent | yes |
| afterShow | A cell showed, restoring its space. | ShellAfterShowEvent | no |
| tabChange | A tabbed cell's active tab changed. | ShellTabChangeEvent | no |
| stateChange | `stateChange`: `setState()` applied a snapshot and the arrangement now matches it. | ShellStateChangeEvent | no |
| beforeResizeStart | A pointer or keyboard resize gesture is about to begin; cancel it to keep the two cells' sizes. | ShellResizeStartEvent | - |
| resize | The two cells' sizes changed during a drag, or after an arrow/Home/End key. | ShellResizeEvent | no |
| afterResizeEnd | A resize gesture ended; the two cells hold their final sizes. | ShellResizeEvent | no |