Layouts & Snapshots

Initial layout, snapshot, size, behavior, edge panel, floating panel, and tab configuration types.

Initial layout, snapshot, and runtime state

Tilery uses three related shapes. They are easy to mix up, so choose the one that matches the job.

ShapeUse it forNotes
TileryInitialLayout<TData>Booting the componentAuthored by your app. Accepts shorthand like locked: true.
TileryLayoutSnapshot<TData>PersistenceReturned by getLayout(). Stores explicit behavior booleans and floating bounds.
TileryLayoutStateRuntime callbacks and inspectionInternal working state exposed to callbacks. Do not persist it.

Initial layouts and snapshots share the same serializable tree idea: groups contain panels or groups, panels contain tabs, and a root layout can also contain pinned edge panels and floating panels. A snapshot preserves the panel tree, edge regions, tab order, active tabs, fullscreen panel, reset targets, size constraints, behavior booleans, floating bounds, popout window bounds, and z-order.

Use getLayout() to create a snapshot and pass that snapshot back to initialLayout or setLayout(layout) to restore it.

Size and behavior rules

size belongs to each child item and controls that item’s allocation inside its parent group. On root edge panels, size controls the pinned side’s width or height as a percentage of the Tilery root. defaultSize is optional and defines where adjacent panels return when the user double-clicks a divider. If you omit defaultSize, Tilery uses the item’s initial size as the reset target.

minSize and maxSize can be numbers, percentage strings, or pixel strings. Divider reset uses the same constraints as drag resizing, so a default target can still be clamped when a panel needs more room.

Initial layouts accept locked: true as shorthand for disabling resize, drag, and drop on a layout item. Tabs accept locked: true as shorthand for disabling close and drag. Snapshots store the explicit booleans directly and do not emit locked.

Type reference

type TilerySize = number | `${number}%` | `${number}px`;

type TileryEdge = 'left' | 'right' | 'top' | 'bottom';

type TileryInitialLayout<TData> =
  | TileryDockedLayoutInit<TData>
  | {
      type: 'root';
      main: TileryDockedLayoutInit<TData>;
      edges?: Partial<Record<TileryEdge, TileryEdgePanelInit<TData>>>;
      floating?: TileryFloatingPanelInit<TData>[];
    };

type TileryDockedLayoutInit<TData> =
  | { type: 'empty' }
  | ({
      type: 'panel';
      id?: string;
      size?: number;
      defaultSize?: number;
      minSize?: TilerySize;
      maxSize?: TilerySize;
      tabs: TileryTabInit<TData>[];
      activeTabId?: string;
      fullScreen?: boolean;
    } & TileryLayoutBehaviorConfig)
  | ({
      type: 'group';
      id?: string;
      direction: 'horizontal' | 'vertical';
      size?: number;
      defaultSize?: number;
      children: TileryDockedLayoutInit<TData>[];
    } & TileryLayoutBehaviorConfig);

type TileryFloatingPanelBounds = {
  x: number;
  y: number;
  width: number;
  height: number;
};

type TileryPopoutWindowBounds = {
  left: number;
  top: number;
  width: number;
  height: number;
};

type TileryFloatingPanelInit<TData> = {
  type: 'floatingPanel';
  id?: string;
  bounds?: Partial<TileryFloatingPanelBounds>;
  zIndex?: number;
  popout?: true | { windowBounds?: Partial<TileryPopoutWindowBounds> };
  tabs: TileryTabInit<TData>[];
  activeTabId?: string;
  fullScreen?: boolean;
  minSize?: TilerySize;
  maxSize?: TilerySize;
} & TileryLayoutBehaviorConfig;

type TileryEdgePanelInit<TData> = {
  type: 'edgePanel';
  id?: string;
  size?: number;
  defaultSize?: number;
  tabs: TileryTabInit<TData>[];
  activeTabId?: string;
  fullScreen?: boolean;
  minSize?: TilerySize;
  maxSize?: TilerySize;
} & TileryLayoutBehaviorConfig;

type TileryLayoutBehaviorConfig =
  | { locked: true; resizable?: never; draggable?: never; droppable?: never }
  | {
      locked?: false;
      resizable?: boolean;
      draggable?: boolean;
      droppable?: boolean;
    };

type TileryLayoutBehavior = {
  resizable: boolean;
  draggable: boolean;
  droppable: boolean;
};

type TileryTabBehaviorConfig =
  | { locked: true; closable?: never; draggable?: never }
  | { locked?: false; closable?: boolean; draggable?: boolean };

type TileryTabBehaviorUpdate =
  | { locked: true; closable?: never; draggable?: never }
  | { locked?: false; closable?: boolean; draggable?: boolean };

type TileryTabInit<TData> = {
  id?: string;
  data: TData;
} & TileryTabBehaviorConfig;

type TileryTabBehavior = {
  closable: boolean;
  draggable: boolean;
};

type TileryTabBehaviorUpdate =
  | { locked: true; closable?: never; draggable?: never }
  | { locked?: false; closable?: boolean; draggable?: boolean };

type TileryTabSnapshot<TData> = {
  id?: string;
  data: TData;
} & TileryTabBehavior;

type TileryLayoutSnapshot<TData> =
  | TileryDockedLayoutSnapshot<TData>
  | {
      type: 'root';
      main: TileryDockedLayoutSnapshot<TData>;
      edges?: Partial<Record<TileryEdge, TileryEdgePanelSnapshot<TData>>>;
      floating: TileryFloatingPanelSnapshot<TData>[];
    };

type TileryDockedLayoutSnapshot<TData> =
  | { type: 'empty' }
  | ({
      type: 'panel';
      id?: string;
      size?: number;
      defaultSize?: number;
      minSize?: TilerySize;
      maxSize?: TilerySize;
      tabs: TileryTabSnapshot<TData>[];
      activeTabId?: string;
      fullScreen?: boolean;
    } & TileryLayoutBehavior)
  | ({
      type: 'group';
      id?: string;
      direction: 'horizontal' | 'vertical';
      size?: number;
      defaultSize?: number;
      children: TileryDockedLayoutSnapshot<TData>[];
    } & TileryLayoutBehavior);

type TileryFloatingPanelSnapshot<TData> = {
  type: 'floatingPanel';
  id?: string;
  bounds: TileryFloatingPanelBounds;
  zIndex: number;
  popout?: { windowBounds: TileryPopoutWindowBounds };
  tabs: TileryTabSnapshot<TData>[];
  activeTabId?: string;
  fullScreen?: boolean;
  minSize?: TilerySize;
  maxSize?: TilerySize;
} & TileryLayoutBehavior;

type TileryEdgePanelSnapshot<TData> = {
  type: 'edgePanel';
  id?: string;
  size: number;
  defaultSize?: number;
  tabs: TileryTabSnapshot<TData>[];
  activeTabId?: string;
  fullScreen?: boolean;
  minSize?: TilerySize;
  maxSize?: TilerySize;
} & TileryLayoutBehavior;