summaryrefslogtreecommitdiffhomepage
path: root/src/features/layout/logic/panels-edit.ts
blob: 8a568595146cb8d8b8a4b9937f4f1d8b68214bb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * Panel-edit → tree-op mapping: the pure decision logic behind TilePanels'
 * dropdown swap / add / remove buttons. The component builds the ops; the
 * renderer's owner (TileRoot → onTreeChange) applies them. Zero Svelte, zero
 * DOM.
 */

import type { LayoutOp, Path } from "@dispatch/layout-contract";

/** Swap the view a panel leaf mounts (panel `index` of the panels node at `path`). */
export function swapPanelOp(path: Path, index: number, view: string): LayoutOp {
  return { type: "replaceView", target: [...path, index], view };
}

/** Add a panel mounting `view` (appended to the panels node at `path`). */
export function addPanelOp(path: Path, view: string): LayoutOp {
  return { type: "insertPanel", panels: path, view };
}

/** Remove the panel at `index` from the panels node at `path`. */
export function removePanelOp(path: Path, index: number): LayoutOp {
  return { type: "removeNode", target: [...path, index] };
}

/**
 * The view a new panel starts with. The tree schema has no "empty panel"
 * state (a leaf's `view` is required — today's "Select a view" placeholder has
 * no tree representation), so the + button pre-selects the first panel-eligible
 * view. Returns null when nothing is eligible (the + button hides).
 */
export function firstPanelView(views: readonly { readonly id: string }[]): string | null {
  return views[0]?.id ?? null;
}