Close: bubble-up DOM hide + key-bump on full collapse
Two improvements to the close-pane workaround:
1. When both children of a parent split are now hidden, the .side hide
bubbles up to the parent split's own .side and hides that too. Needed
for deeply-nested splits where closing leaves at the bottom should
propagate the visual collapse upward.
2. When closeLeaf returns null (the user closed the last remaining
leaf), force a full Pane remount via {#key renderKey} bump. The
DOM-hide approach can't simulate mounting a fresh tree node, so this
is the one place where we take the cost of a full unmount + remount.
Only fires when the entire tree resets — not on intermediate closes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8d5c49155b
commit
40ce27251d
1 changed files with 39 additions and 8 deletions
|
|
@ -56,6 +56,11 @@
|
||||||
if (activeLeafId === id) activeLeafId = null;
|
if (activeLeafId === id) activeLeafId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bumped to force a full Pane unmount+remount when the tree's structure
|
||||||
|
// changes in a way the DOM-hide workaround can't simulate (specifically,
|
||||||
|
// when ALL panes have been closed and we need to render a fresh leaf).
|
||||||
|
let renderKey = $state(0);
|
||||||
|
|
||||||
// ---- tree mutation handlers (closures over tree $state) -----------------
|
// ---- tree mutation handlers (closures over tree $state) -----------------
|
||||||
function handleSplit(leafId: NodeId, orientation: Orientation) {
|
function handleSplit(leafId: NodeId, orientation: Orientation) {
|
||||||
const parent = findLeaf(tree, leafId);
|
const parent = findLeaf(tree, leafId);
|
||||||
|
|
@ -73,21 +78,45 @@
|
||||||
void killPane(paneId).catch((e) => console.warn("killPane failed:", e));
|
void killPane(paneId).catch((e) => console.warn("killPane failed:", e));
|
||||||
orch.paneIdByLeaf.delete(leafId);
|
orch.paneIdByLeaf.delete(leafId);
|
||||||
}
|
}
|
||||||
// Hide the closed pane's flex container and the adjacent gutter so the
|
|
||||||
// sibling pane visually fills the freed space.
|
// DOM-hide the .side wrapping this leaf and the adjacent gutter so the
|
||||||
|
// sibling pane visually fills. If both sides of the parent split are
|
||||||
|
// now hidden, bubble up and hide that whole split too (recursive
|
||||||
|
// collapse — needed for nested closes).
|
||||||
const leafEl = document.querySelector(`[data-leaf-id="${leafId}"]`);
|
const leafEl = document.querySelector(`[data-leaf-id="${leafId}"]`);
|
||||||
const sideEl = leafEl?.closest(".side") as HTMLElement | null;
|
let sideEl = leafEl?.closest(".side") as HTMLElement | null;
|
||||||
const splitEl = sideEl?.parentElement;
|
while (sideEl) {
|
||||||
if (sideEl && splitEl) {
|
|
||||||
sideEl.style.display = "none";
|
sideEl.style.display = "none";
|
||||||
|
const splitEl = sideEl.parentElement;
|
||||||
|
if (!splitEl) break;
|
||||||
|
// Hide the gutter in this split (only one).
|
||||||
Array.from(splitEl.children).forEach((c) => {
|
Array.from(splitEl.children).forEach((c) => {
|
||||||
const child = c as HTMLElement;
|
const child = c as HTMLElement;
|
||||||
if (child.classList.contains("gutter")) child.style.display = "none";
|
if (child.classList.contains("gutter")) child.style.display = "none";
|
||||||
});
|
});
|
||||||
|
// If a sibling side is still visible, flex will auto-fill; we're done.
|
||||||
|
const visibleSiblings = Array.from(splitEl.children).filter((c) => {
|
||||||
|
const child = c as HTMLElement;
|
||||||
|
return (
|
||||||
|
child.classList.contains("side") &&
|
||||||
|
child.style.display !== "none"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (visibleSiblings.length > 0) break;
|
||||||
|
// Otherwise, this whole split is empty — climb up and hide its parent side.
|
||||||
|
sideEl = splitEl.closest(".side") as HTMLElement | null;
|
||||||
}
|
}
|
||||||
// Update tree state (used by broadcast routing, palette, persistence).
|
|
||||||
|
// Update tree state for persistence / palette / broadcast routing.
|
||||||
const next = closeLeaf(tree, leafId);
|
const next = closeLeaf(tree, leafId);
|
||||||
tree = next ?? newLeaf({ distro: defaultDistro });
|
if (next === null) {
|
||||||
|
// Tree fully collapsed. The DOM-hide workaround can't simulate
|
||||||
|
// mounting a brand-new leaf, so force a clean remount via key bump.
|
||||||
|
tree = newLeaf({ distro: defaultDistro });
|
||||||
|
renderKey += 1;
|
||||||
|
} else {
|
||||||
|
tree = next;
|
||||||
|
}
|
||||||
clearActiveIf(leafId);
|
clearActiveIf(leafId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,7 +361,9 @@
|
||||||
|
|
||||||
<div class="pane-wrap">
|
<div class="pane-wrap">
|
||||||
{#if ready}
|
{#if ready}
|
||||||
|
{#key renderKey}
|
||||||
<Pane node={tree} {activeLeafId} />
|
<Pane node={tree} {activeLeafId} />
|
||||||
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue