summaryrefslogtreecommitdiffhomepage
path: root/src/features/layout/logic/panels-edit.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-05 00:34:45 +0900
committerAdam Malczewski <[email protected]>2026-09-05 00:34:45 +0900
commita104c094b588560f955ff52dc7f949cf3c355f94 (patch)
tree61983d5568f47fd23739705aaec815be1336b595 /src/features/layout/logic/panels-edit.ts
parent60a726883be85c174e88ae832ef95f3c0f084ec9 (diff)
downloaddispatch-web-a104c094b588560f955ff52dc7f949cf3c355f94.tar.gz
dispatch-web-a104c094b588560f955ff52dc7f949cf3c355f94.zip
feat(layout): plan 01 — tile engine + view registry
Diffstat (limited to 'src/features/layout/logic/panels-edit.ts')
-rw-r--r--src/features/layout/logic/panels-edit.ts33
1 files changed, 33 insertions, 0 deletions
diff --git a/src/features/layout/logic/panels-edit.ts b/src/features/layout/logic/panels-edit.ts
new file mode 100644
index 0000000..8a56859
--- /dev/null
+++ b/src/features/layout/logic/panels-edit.ts
@@ -0,0 +1,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;
+}