Skip to content

Commit

Permalink
Force eager layout re-calculation after panel added/removed (#375)
Browse files Browse the repository at this point in the history
Resolves #372
  • Loading branch information
bvaughn authored Jul 21, 2024
1 parent e3c4ed6 commit 87c0849
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 31 deletions.
74 changes: 43 additions & 31 deletions packages/react-resizable-panels/src/PanelGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EXCEEDED_VERTICAL_MIN,
reportConstraintsViolation,
} from "./PanelResizeHandleRegistry";
import { useForceUpdate } from "./hooks/useForceUpdate";
import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
import useUniqueId from "./hooks/useUniqueId";
import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterPanelGroupBehavior";
Expand Down Expand Up @@ -123,6 +124,7 @@ function PanelGroupWithForwardedRef({
const panelGroupElementRef = useRef<HTMLDivElement | null>(null);
const [dragState, setDragState] = useState<DragState | null>(null);
const [layout, setLayout] = useState<number[]>([]);
const forceUpdate = useForceUpdate();

const panelIdToLastNotifiedSizeMapRef = useRef<Record<string, number>>({});
const panelSizeBeforeCollapseRef = useRef<Map<string, number>>(new Map());
Expand Down Expand Up @@ -521,26 +523,31 @@ function PanelGroupWithForwardedRef({
return !collapsible || fuzzyCompareNumbers(panelSize, collapsedSize) > 0;
}, []);

const registerPanel = useCallback((panelData: PanelData) => {
const { panelDataArray } = eagerValuesRef.current;
const registerPanel = useCallback(
(panelData: PanelData) => {
const { panelDataArray } = eagerValuesRef.current;

panelDataArray.push(panelData);
panelDataArray.sort((panelA, panelB) => {
const orderA = panelA.order;
const orderB = panelB.order;
if (orderA == null && orderB == null) {
return 0;
} else if (orderA == null) {
return -1;
} else if (orderB == null) {
return 1;
} else {
return orderA - orderB;
}
});
panelDataArray.push(panelData);
panelDataArray.sort((panelA, panelB) => {
const orderA = panelA.order;
const orderB = panelB.order;
if (orderA == null && orderB == null) {
return 0;
} else if (orderA == null) {
return -1;
} else if (orderB == null) {
return 1;
} else {
return orderA - orderB;
}
});

eagerValuesRef.current.panelDataArrayChanged = true;
}, []);
eagerValuesRef.current.panelDataArrayChanged = true;

forceUpdate();
},
[forceUpdate]
);

// (Re)calculate group layout whenever panels are registered or unregistered.
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -844,22 +851,27 @@ function PanelGroupWithForwardedRef({
setDragState(null);
}, []);

const unregisterPanel = useCallback((panelData: PanelData) => {
const { panelDataArray } = eagerValuesRef.current;
const unregisterPanel = useCallback(
(panelData: PanelData) => {
const { panelDataArray } = eagerValuesRef.current;

const index = findPanelDataIndex(panelDataArray, panelData);
if (index >= 0) {
panelDataArray.splice(index, 1);
const index = findPanelDataIndex(panelDataArray, panelData);
if (index >= 0) {
panelDataArray.splice(index, 1);

// TRICKY
// When a panel is removed from the group, we should delete the most recent prev-size entry for it.
// If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
// Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
delete panelIdToLastNotifiedSizeMapRef.current[panelData.id];
// TRICKY
// When a panel is removed from the group, we should delete the most recent prev-size entry for it.
// If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
// Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
delete panelIdToLastNotifiedSizeMapRef.current[panelData.id];

eagerValuesRef.current.panelDataArrayChanged = true;
}
}, []);
eagerValuesRef.current.panelDataArrayChanged = true;

forceUpdate();
}
},
[forceUpdate]
);

const context = useMemo(
() =>
Expand Down
7 changes: 7 additions & 0 deletions packages/react-resizable-panels/src/hooks/useForceUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useCallback, useState } from "../vendor/react";

export function useForceUpdate() {
const [_, setCount] = useState(0);

return useCallback(() => setCount((prevCount) => prevCount + 1), []);
}

0 comments on commit 87c0849

Please sign in to comment.