Skip to content
D
Documentation

Layout — Grid Pack

reference
7 min readUpdated

Import these from @grafloria/engine.

Classes

GridPackEngine

ts
class GridPackEngine

Properties

NameTypeDefaultDescription
floatboolean
maxRows?numberRow bound (see GridPackOptions.maxRows). Undefined = unbounded. Changed through {@link setBound}.
capacity?numberRow capacity (see GridPackOptions.capacity). Undefined = unbounded.

Methods

  • get columns(): number — Live column count. Change it only through {@link setColumns}.
  • constructor(items: GridPackItem[] = [], options: GridPackOptions = {})
  • getItems(): readonly GridPackItem[]
  • getItem(id: string): GridPackItem | undefined
  • rows(): number — Content height in rows: max(y+h) over all items (0 when empty).
  • hasOverlaps(): boolean — True when any two items overlap — the invariant every op must preserve.
  • add(item: GridPackItem): GridPackItem | null — Add an item. An explicit legal position is honoured verbatim. When the position collides — or the item asks for autoPosition — the tile AUTO-POSITIONS: a row-major scan for the first hole it fits, which is gridstack's autoPosition and NOT the same thing as gravity (gravity climbs one column; a hole at (3,0) under an occupied column is only reachable by the scan — the spec's first red proved it).
  • remove(id: string): void
  • beginGesture(): void — Begin a drag/resize gesture: fresh displaced-tile memory, and a full snapshot so cancelGesture (Escape) can restore every tile (gridstack saveInitial / restoreInitial).
  • endGesture(): void — End a gesture: memory does NOT outlive it (E2, and the S4 swap lock).
  • cancelGesture(): void — Escape: restore every tile to its gesture-start cell AND size, bring back the tiles removed during the gesture, drop the ones added, and restore the bound. Surviving tiles keep their object identity (a binder holds them).
  • setBound(rows: number): boolean — Change the row bound — a container asking for rows on behalf of a child (tile first, step 1). Refused below the current content. Inside a gesture the change rides in the snapshot, so Escape gives the rows back.
  • moveCheck(id: string, x: number, y: number, options: MoveCheckOptions = {}): GridPackResult — Try to put id at cell (x,y). Applies the full pipeline on acceptance: swap (three shapes) | push-down (+skip below locked) → settle (teleport memory + gravity). Refuses: out-of-gesture no-ops, cells intersecting a locked tile (E4b), and collisions under the anti-jitter coverage gate. { gate: false } (see {@link MoveCheckOptions}) is first-placement mode: gate and swaps are skipped, push-down still applies, E4b still refuses.
  • resizeCheck(id: string, w: number, h: number, options: ResizeCheckOptions = {}): GridPackResult — Resize id to w×h cells. Growth is CLAMPED so the tile never covers a locked tile (E4b applied to size); displaced neighbours push + settle, and return when the size shrinks back (E1/S2).
  • placeBeside(id: string, neighbourId: string, side: BesideSide, row?: number): PlaceBesideResult — Put id NEXT TO neighbourId on side, at row (the pointer's row, clamped to the neighbour's rows; the neighbour's own row by default) — the one sideways primitive the tile-first drag model needs (step 1).
  • setColumns(next: number, layout: GridColumnLayout = 'moveScale'): boolean — Change the board's COLUMN COUNT — gridstack's column(n, layout), and the whole of responsive behaviour in one operation.
  • saveLayout(): { columns: number; items: GridPackItem[] } — The layout to PERSIST — gridstack's save(), which serialises from the LARGEST cached column count rather than the live one. Saving while the board is narrow (a phone) therefore saves the DESKTOP layout: the cells a user authored at full width are the document, and the narrow arrangement is a projection of it.
  • cachedColumns(): number[] — The column counts the cache currently holds a layout for (ascending).
  • getLayouts(): GridLayoutCache — The cache as plain data — hand it to a rebuilt engine via {@link setLayouts}.
  • setLayouts(cache: GridLayoutCache): void — Adopt a cache exported by {@link getLayouts}. The kit calls this after rebuilding its engine from the model (sync()), which would otherwise throw the wider layouts away on every undo, add or refresh.

Interfaces

CachedCell

One item's cached geometry at a column count. h is column-independent.

ts
interface CachedCell

Properties

NameTypeDefaultDescription
xnumber
ynumber
wnumber

GridPackItem

One tile, in integer grid cells.

ts
interface GridPackItem

Properties

NameTypeDefaultDescription
idstring
xnumber
ynumber
wnumber
hnumber
locked?booleanPinned: never pushed, never packed, refuses the mover outright (E4b).
solid?booleanSOLID (tile first, step 3): a container. Never pushed by a PASSING tile and never packed — a widget carried over it slides aside, so the board under the hand holds still and the container's zones stay reachable — but moved by INTENT: a mover that asks pushSolid (a moved section, a dock, a refused adoption), placeBeside, or a direct move of its own.
minW?numberPer-item SIZE LIMITS in cells (gridstack's minW/maxW/minH/maxH). A resize clamps to them, and a column change scales a width only within them. They never move a tile: a push or a swap is not a resize.
maxW?number
minH?number
maxH?number
autoPosition?booleanAsk add() to IGNORE x/y and scan row-major for the first free hole — gridstack's autoPosition (addWidget without coordinates). Distinct from gravity, which climbs one column: a hole at (3,0) under an occupied column is only reachable by the scan. Seeding/load honours explicit cells; palette-style adds pass this flag.

GridPackOptions

ts
interface GridPackOptions

Properties

NameTypeDefaultDescription
columns?numberColumn count of the board. Default 12.
float?booleanFloat mode (gridstack float: true): tiles stay where placed and gaps are legal; gravity does not pack. Default false (gravity).
maxRows?numberRow bound for a board whose DESIGN is a fixed strip (the nested KPI section: one row, always). Any op whose settled result would exceed it — a height resize, a width resize whose push spills a sibling down, a move displacing someone out of bounds — ROLLS BACK wholesale and reports changed: false; add() returns null when nothing can fit, so growing a first-row KPI can never push the strip past …
capacity?numberRow CAPACITY of a board whose frame can hold only so many rows — the dashboard kit's bounded fit mode (a fit board never changes size; past its row floor it refuses rather than overflows).

GridPackResult

Result of a move/resize attempt.

ts
interface GridPackResult

Properties

NameTypeDefaultDescription
changedbooleanWhether the board accepted (and applied) the change.
refusedBy?GridPackRefusalPresent on every refusal, absent on an accepted change.

MoveCheckOptions

Options for {@link GridPackEngine.moveCheck}.

ts
interface MoveCheckOptions

Properties

NameTypeDefaultDescription
gate?booleanThe >50% anti-jitter coverage gate AND the swap heuristics it feeds. gate: false is the FIRST-PLACEMENT mode (a palette drag-in entering the board, or a dragged-out tile re-entering — gridstack's dragInNode behaviour): the cell is taken unconditionally — locked tiles still refuse (E4b), but any unlocked occupant is pushed down regardless of coverage, and no swap shape fires (an entering tile …
pushSolid?booleanPush SOLID tiles in the way as if they were plain — the mover means it (a moved section, a dock, a refused adoption). Default false.

PlaceBesideResult

Also has every member of GridPackResult, listed on its own entry.

Result of {@link GridPackEngine.placeBeside}.

ts
interface PlaceBesideResult extends GridPackResult

Properties

NameTypeDefaultDescription
how?'placed' | 'shifted' | 'pushed'placed: the cell beside the neighbour was free (or only unlocked tiles were in it, pushed down); shifted: no room on that side of the board, the neighbour moved over by the mover's span and the mover took the edge; pushed: no room even shifted — the mover took the cell and the neighbour went down under it, the way any tile gives way.

ResizeCheckOptions

Options for {@link GridPackEngine.resizeCheck}.

ts
interface ResizeCheckOptions

Properties

NameTypeDefaultDescription
pushSolid?booleanGrow INTO solid tiles, pushing them, instead of clamping at them — a dock taking its band means it. Default false.

Types

BesideSide

Also has every member of String, listed on its own entry.

The side of a neighbour a tile is placed against.

ts
type BesideSide = 'left' | 'right' | 'top' | 'bottom';

GridColumnLayout

Also has every member of String, listed on its own entry.

How a COLUMN-COUNT change re-lays the board out — gridstack's ColumnOptions, recorded from its documented semantics:

'moveScale' (default) — scale both x and w by newColumns/oldColumns, so the board keeps its proportions at any width; 'move' — scale x only; widths survive verbatim (clamped to fit); 'scale' — scale w only; x survives verbatim (clamped to fit); 'none' — keep x and w exactly; clamp only what no longer fits.

Whatever the mode, columns === 1 forces a SINGLE STACK (every tile x=0, w=1) — the phone layout — and every mode re-places the result in reading order afterwards, so the outcome can never overlap.

ts
type GridColumnLayout = 'moveScale' | 'move' | 'scale' | 'none';

GridLayoutCache

The PER-COLUMN LAYOUT CACHE, as plain data: column count → item id → cell. Exported/imported so a host that rebuilds its engine (the kit's sync()) carries the wider layouts across the rebuild instead of losing them.

ts
type GridLayoutCache = Record<number, Record<string, CachedCell>>;

GridPackRefusal

Also has every member of String, listed on its own entry.

Why a change was refused (tile first, step 1). changed: false alone can also mean "already there", so every refusal names itself.

ts
type GridPackRefusal = 'locked' | 'solid' | 'bound' | 'gate' | 'noop' | 'missing';

Was this page helpful?