Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 68 additions & 10 deletions lib/global/utils/getImperativePanelMethods.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { PanelImperativeHandle } from "../../components/panel/types";
import type { Layout } from "../../components/group/types";
import type {
PanelConstraints,
PanelImperativeHandle,
RegisteredPanel
} from "../../components/panel/types";
import { calculateAvailableGroupSize } from "../dom/calculateAvailableGroupSize";
import { getMountedGroups, updateMountedGroup } from "../mutable-state/groups";
import { sizeStyleToPixels } from "../styles/sizeStyleToPixels";
Expand Down Expand Up @@ -71,6 +76,64 @@ export function getImperativePanelMethods({
throw Error(`Layout not found for Panel ${panelId}`);
};

/**
* Compute the next (unvalidated) layout when resizing a panel imperatively.
*
* Handles two edge cases for the last panel:
* 1. Single panel in the group — no sibling exists to form valid pivot indices.
* 2. All preceding panels are already collapsed — the normal reversed-delta
* logic would cascade the freed space to the first panel. Instead the last
* panel keeps the remainder so it stays the largest.
*/
const computeLayout = ({
nextSize,
panels,
prevLayout,
derivedPanelConstraints
}: {
nextSize: number;
panels: RegisteredPanel[];
prevLayout: Layout;
derivedPanelConstraints: PanelConstraints[];
}): Layout => {
const prevSize = getPanelSize();

const index = panels.findIndex((current) => current.id === panelId);
const isFirstPanel = index === 0;
const isLastPanel = index === panels.length - 1;

const allPreviousCollapsed =
isLastPanel &&
nextSize < prevSize &&
(isFirstPanel ||
Copy link
Copy Markdown
Author

@hugs7 hugs7 Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed this assertion error can happen when collapsing the one and only panel in a group, since the delta value otherwise goes to 100 causing it to index at -1 here at adjustLayoutByDelta on line 142

Image

Copy link
Copy Markdown
Author

@hugs7 hugs7 Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've addressed this in this PR via the following logic: If there's only one panel in the group, we can still collapse it but leave it's flex-grow value at 100%

panels.slice(0, index).every((_panel, panelIndex) => {
const pc = derivedPanelConstraints[panelIndex];
return (
pc?.collapsible &&
layoutNumbersEqual(pc.collapsedSize, prevLayout[pc.panelId])
);
}));

if (allPreviousCollapsed) {
const occupiedByPrevious = panels
.slice(0, index)
.reduce((total, panel) => total + prevLayout[panel.id], 0);
return {
...prevLayout,
[panelId]: formatLayoutNumber(100 - occupiedByPrevious)
};
}

return adjustLayoutByDelta({
delta: isLastPanel ? prevSize - nextSize : nextSize - prevSize,
initialLayout: prevLayout,
panelConstraints: derivedPanelConstraints,
pivotIndices: isLastPanel ? [index - 1, index] : [index, index + 1],
prevLayout,
trigger: "imperative-api"
});
};

const setPanelSize = (nextSize: number) => {
const prevSize = getPanelSize();
if (nextSize === prevSize) {
Expand All @@ -86,16 +149,11 @@ export function getImperativePanelMethods({
separatorToPanels
} = find();

const index = group.panels.findIndex((current) => current.id === panelId);
const isLastPanel = index === group.panels.length - 1;

const unsafeLayout = adjustLayoutByDelta({
delta: isLastPanel ? prevSize - nextSize : nextSize - prevSize,
initialLayout: prevLayout,
panelConstraints: derivedPanelConstraints,
pivotIndices: isLastPanel ? [index - 1, index] : [index, index + 1],
const unsafeLayout = computeLayout({
nextSize,
panels: group.panels,
prevLayout,
trigger: "imperative-api"
derivedPanelConstraints
});

const nextLayout = validatePanelGroupLayout({
Expand Down
Loading