react-resizable-panels 0.0.51 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.53
4
+ * Fix edge case race condition for `onResize` callbacks during initial mount
5
+
6
+ ## 0.0.52
7
+ * [162](https://github.com/bvaughn/react-resizable-panels/issues/162): Add `Panel.collapsedSize` property to allow panels to be collapsed to custom, non-0 sizes
8
+ * [161](https://github.com/bvaughn/react-resizable-panels/pull/161): Bug fix: `onResize` should be called for the initial `Panel` size regardless of the `onLayout` prop
9
+
3
10
  ## 0.0.51
4
11
  * [154](https://github.com/bvaughn/react-resizable-panels/issues/154): `onResize` and `onCollapse` props are called in response to `PanelGroup.setLayout`
5
12
  * [123](https://github.com/bvaughn/react-resizable-panels/issues/123): `onResize` called when number of panels in a group change due to conditional rendering
package/README.md CHANGED
@@ -49,20 +49,21 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
49
49
  | `setLayout(panelSizes: number[])` | Resize panel group to the specified _panelSizes_ (`[1 - 100, ...]`).
50
50
 
51
51
  ### `Panel`
52
- | prop | type | description
53
- | :------------ | :------------------------------ | :---
54
- | `children` | `ReactNode` | Arbitrary React element(s)
55
- | `className` | `?string` | Class name to attach to root element
56
- | `collapsible` | `?boolean=false` | Panel should collapse when resized beyond its `minSize`
57
- | `defaultSize` | `?number` | Initial size of panel (numeric value between 1-100)
58
- | `id` | `?string` | Panel id (unique within group); falls back to `useId` when not provided
59
- | `maxSize` | `?number = 100` | Maximum allowable size of panel (numeric value between 1-100); defaults to `100`
60
- | `minSize` | `?number = 10` | Minimum allowable size of panel (numeric value between 1-100); defaults to `10`
61
- | `onCollapse` | `?(collapsed: boolean) => void` | Called when panel is collapsed; `collapsed` boolean parameter reflecting the new state
62
- | `onResize` | `?(size: number) => void` | Called when panel is resized; `size` parameter is a numeric value between 1-100. <sup>1</sup>
63
- | `order` | `?number` | Order of panel within group; required for groups with conditionally rendered panels
64
- | `style` | `?CSSProperties` | CSS style to attach to root element
65
- | `tagName` | `?string = "div"` | HTML element tag name for root element
52
+ | prop | type | description
53
+ | :-------------- | :------------------------------ | :---
54
+ | `children` | `ReactNode` | Arbitrary React element(s)
55
+ | `className` | `?string` | Class name to attach to root element
56
+ | `collapsedSize` | `?number=0` | Panel should collapse to this size
57
+ | `collapsible` | `?boolean=false` | Panel should collapse when resized beyond its `minSize`
58
+ | `defaultSize` | `?number` | Initial size of panel (numeric value between 1-100)
59
+ | `id` | `?string` | Panel id (unique within group); falls back to `useId` when not provided
60
+ | `maxSize` | `?number = 100` | Maximum allowable size of panel (numeric value between 1-100); defaults to `100`
61
+ | `minSize` | `?number = 10` | Minimum allowable size of panel (numeric value between 1-100); defaults to `10`
62
+ | `onCollapse` | `?(collapsed: boolean) => void` | Called when panel is collapsed; `collapsed` boolean parameter reflecting the new state
63
+ | `onResize` | `?(size: number) => void` | Called when panel is resized; `size` parameter is a numeric value between 1-100. <sup>1</sup>
64
+ | `order` | `?number` | Order of panel within group; required for groups with conditionally rendered panels
65
+ | `style` | `?CSSProperties` | CSS style to attach to root element
66
+ | `tagName` | `?string = "div"` | HTML element tag name for root element
66
67
 
67
68
  <sup>1</sup>: If any `Panel` has an `onResize` callback, the `order` prop should be provided for all `Panel`s.
68
69
 
@@ -3,6 +3,7 @@ import { PanelOnCollapse, PanelOnResize } from "./types";
3
3
  export type PanelProps = {
4
4
  children?: ReactNode;
5
5
  className?: string;
6
+ collapsedSize?: number;
6
7
  collapsible?: boolean;
7
8
  defaultSize?: number | null;
8
9
  id?: string | null;
@@ -6,7 +6,7 @@ export type PanelGroupStorage = {
6
6
  };
7
7
  export type PanelGroupOnLayout = (sizes: number[]) => void;
8
8
  export type PanelOnCollapse = (collapsed: boolean) => void;
9
- export type PanelOnResize = (size: number) => void;
9
+ export type PanelOnResize = (size: number, prevSize: number) => void;
10
10
  export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
11
11
  export type PanelCallbackRef = RefObject<{
12
12
  onCollapse: PanelOnCollapse | null;
@@ -15,6 +15,7 @@ export type PanelCallbackRef = RefObject<{
15
15
  export type PanelData = {
16
16
  current: {
17
17
  callbacksRef: PanelCallbackRef;
18
+ collapsedSize: number;
18
19
  collapsible: boolean;
19
20
  defaultSize: number | null;
20
21
  id: string;
@@ -69,6 +69,7 @@ PanelGroupContext.displayName = "PanelGroupContext";
69
69
  function PanelWithForwardedRef({
70
70
  children = null,
71
71
  className: classNameFromProps = "",
72
+ collapsedSize = 0,
72
73
  collapsible = false,
73
74
  defaultSize = null,
74
75
  forwardedRef,
@@ -126,6 +127,7 @@ function PanelWithForwardedRef({
126
127
  });
127
128
  const panelDataRef = useRef({
128
129
  callbacksRef,
130
+ collapsedSize,
129
131
  collapsible,
130
132
  defaultSize,
131
133
  id: panelId,
@@ -136,6 +138,7 @@ function PanelWithForwardedRef({
136
138
  useIsomorphicLayoutEffect(() => {
137
139
  committedValuesRef.current.size = parseSizeFromStyle(style);
138
140
  panelDataRef.current.callbacksRef = callbacksRef;
141
+ panelDataRef.current.collapsedSize = collapsedSize;
139
142
  panelDataRef.current.collapsible = collapsible;
140
143
  panelDataRef.current.defaultSize = defaultSize;
141
144
  panelDataRef.current.id = panelId;
@@ -283,11 +286,18 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
283
286
  }
284
287
  function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
285
288
  sizes.forEach((size, index) => {
289
+ const panelRef = panelsArray[index];
290
+ if (!panelRef) {
291
+ // Handle initial mount (when panels are registered too late to be in the panels array)
292
+ // The subsequent render+effects will handle the resize notification
293
+ return;
294
+ }
286
295
  const {
287
296
  callbacksRef,
297
+ collapsedSize,
288
298
  collapsible,
289
299
  id
290
- } = panelsArray[index].current;
300
+ } = panelRef.current;
291
301
  const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
292
302
  if (lastNotifiedSize !== size) {
293
303
  panelIdToLastNotifiedSizeMap[id] = size;
@@ -296,14 +306,12 @@ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
296
306
  onResize
297
307
  } = callbacksRef.current;
298
308
  if (onResize) {
299
- onResize(size);
309
+ onResize(size, lastNotifiedSize);
300
310
  }
301
311
  if (collapsible && onCollapse) {
302
- // Falsy check handles both previous size of 0
303
- // and initial size of undefined (when mounting)
304
- if (!lastNotifiedSize && size !== 0) {
312
+ if ((lastNotifiedSize == null || lastNotifiedSize === collapsedSize) && size !== collapsedSize) {
305
313
  onCollapse(false);
306
- } else if (lastNotifiedSize !== 0 && size === 0) {
314
+ } else if (lastNotifiedSize !== collapsedSize && size === collapsedSize) {
307
315
  onCollapse(true);
308
316
  }
309
317
  }
@@ -395,11 +403,17 @@ function panelsMapToSortedArray(panels) {
395
403
  }
396
404
  function safeResizePanel(panel, delta, prevSize, event) {
397
405
  const nextSizeUnsafe = prevSize + delta;
398
- if (panel.current.collapsible) {
399
- if (prevSize > 0) {
406
+ const {
407
+ collapsedSize,
408
+ collapsible,
409
+ maxSize,
410
+ minSize
411
+ } = panel.current;
412
+ if (collapsible) {
413
+ if (prevSize > collapsedSize) {
400
414
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
401
- if (nextSizeUnsafe <= panel.current.minSize / 2) {
402
- return 0;
415
+ if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
416
+ return collapsedSize;
403
417
  }
404
418
  } else {
405
419
  const isKeyboardEvent = event?.type?.startsWith("key");
@@ -407,13 +421,13 @@ function safeResizePanel(panel, delta, prevSize, event) {
407
421
  // Keyboard events should expand a collapsed panel to the min size,
408
422
  // but mouse events should wait until the panel has reached its min size
409
423
  // to avoid a visual flickering when dragging between collapsed and min size.
410
- if (nextSizeUnsafe < panel.current.minSize) {
411
- return 0;
424
+ if (nextSizeUnsafe < minSize) {
425
+ return collapsedSize;
412
426
  }
413
427
  }
414
428
  }
415
429
  }
416
- const nextSize = Math.min(panel.current.maxSize, Math.max(panel.current.minSize, nextSizeUnsafe));
430
+ const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
417
431
  return nextSize;
418
432
  }
419
433
 
@@ -869,8 +883,8 @@ function PanelGroupWithForwardedRef({
869
883
  } = committedValuesRef.current;
870
884
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
871
885
  const panelsArray = panelsMapToSortedArray(panels);
872
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
873
886
  setSizes(sizes);
887
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
874
888
  }
875
889
  }), []);
876
890
  useIsomorphicLayoutEffect(() => {
@@ -892,25 +906,25 @@ function PanelGroupWithForwardedRef({
892
906
  const {
893
907
  onLayout
894
908
  } = callbacksRef.current;
895
- if (onLayout) {
896
- const {
897
- panels,
898
- sizes
899
- } = committedValuesRef.current;
909
+ const {
910
+ panels,
911
+ sizes
912
+ } = committedValuesRef.current;
900
913
 
901
- // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
902
- if (sizes.length > 0) {
914
+ // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
915
+ if (sizes.length > 0) {
916
+ if (onLayout) {
903
917
  onLayout(sizes);
904
- const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
905
-
906
- // When possible, we notify before the next render so that rendering work can be batched together.
907
- // Some cases are difficult to detect though,
908
- // for example– panels that are conditionally rendered can affect the size of neighboring panels.
909
- // In this case, the best we can do is notify on commit.
910
- // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
911
- const panelsArray = panelsMapToSortedArray(panels);
912
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
913
918
  }
919
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
920
+
921
+ // When possible, we notify before the next render so that rendering work can be batched together.
922
+ // Some cases are difficult to detect though,
923
+ // for example– panels that are conditionally rendered can affect the size of neighboring panels.
924
+ // In this case, the best we can do is notify on commit.
925
+ // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
926
+ const panelsArray = panelsMapToSortedArray(panels);
927
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
914
928
  }
915
929
  }, [sizes]);
916
930
 
@@ -1075,10 +1089,11 @@ function PanelGroupWithForwardedRef({
1075
1089
  }
1076
1090
  if (sizesChanged) {
1077
1091
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1092
+ setSizes(nextSizes);
1078
1093
 
1079
1094
  // If resize change handlers have been declared, this is the time to call them.
1095
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1080
1096
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1081
- setSizes(nextSizes);
1082
1097
  }
1083
1098
  prevDeltaRef.current = delta;
1084
1099
  };
@@ -1100,7 +1115,14 @@ function PanelGroupWithForwardedRef({
1100
1115
  sizes: prevSizes
1101
1116
  } = committedValuesRef.current;
1102
1117
  const panel = panels.get(id);
1103
- if (panel == null || !panel.current.collapsible) {
1118
+ if (panel == null) {
1119
+ return;
1120
+ }
1121
+ const {
1122
+ collapsedSize,
1123
+ collapsible
1124
+ } = panel.current;
1125
+ if (!collapsible) {
1104
1126
  return;
1105
1127
  }
1106
1128
  const panelsArray = panelsMapToSortedArray(panels);
@@ -1109,7 +1131,7 @@ function PanelGroupWithForwardedRef({
1109
1131
  return;
1110
1132
  }
1111
1133
  const currentSize = prevSizes[index];
1112
- if (currentSize === 0) {
1134
+ if (currentSize === collapsedSize) {
1113
1135
  // Panel is already collapsed.
1114
1136
  return;
1115
1137
  }
@@ -1119,14 +1141,15 @@ function PanelGroupWithForwardedRef({
1119
1141
  return;
1120
1142
  }
1121
1143
  const isLastPanel = index === panelsArray.length - 1;
1122
- const delta = isLastPanel ? currentSize : 0 - currentSize;
1144
+ const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1123
1145
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1124
1146
  if (prevSizes !== nextSizes) {
1125
1147
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1148
+ setSizes(nextSizes);
1126
1149
 
1127
1150
  // If resize change handlers have been declared, this is the time to call them.
1151
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1128
1152
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1129
- setSizes(nextSizes);
1130
1153
  }
1131
1154
  }, []);
1132
1155
  const expandPanel = useCallback(id => {
@@ -1138,7 +1161,11 @@ function PanelGroupWithForwardedRef({
1138
1161
  if (panel == null) {
1139
1162
  return;
1140
1163
  }
1141
- const sizeBeforeCollapse = panelSizeBeforeCollapse.current.get(id) || panel.current.minSize;
1164
+ const {
1165
+ collapsedSize,
1166
+ minSize
1167
+ } = panel.current;
1168
+ const sizeBeforeCollapse = panelSizeBeforeCollapse.current.get(id) || minSize;
1142
1169
  if (!sizeBeforeCollapse) {
1143
1170
  return;
1144
1171
  }
@@ -1148,7 +1175,7 @@ function PanelGroupWithForwardedRef({
1148
1175
  return;
1149
1176
  }
1150
1177
  const currentSize = prevSizes[index];
1151
- if (currentSize !== 0) {
1178
+ if (currentSize !== collapsedSize) {
1152
1179
  // Panel is already expanded.
1153
1180
  return;
1154
1181
  }
@@ -1157,14 +1184,15 @@ function PanelGroupWithForwardedRef({
1157
1184
  return;
1158
1185
  }
1159
1186
  const isLastPanel = index === panelsArray.length - 1;
1160
- const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
1187
+ const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1161
1188
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1162
1189
  if (prevSizes !== nextSizes) {
1163
1190
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1191
+ setSizes(nextSizes);
1164
1192
 
1165
1193
  // If resize change handlers have been declared, this is the time to call them.
1194
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1166
1195
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1167
- setSizes(nextSizes);
1168
1196
  }
1169
1197
  }, []);
1170
1198
  const resizePanel = useCallback((id, nextSize) => {
@@ -1176,6 +1204,12 @@ function PanelGroupWithForwardedRef({
1176
1204
  if (panel == null) {
1177
1205
  return;
1178
1206
  }
1207
+ const {
1208
+ collapsedSize,
1209
+ collapsible,
1210
+ maxSize,
1211
+ minSize
1212
+ } = panel.current;
1179
1213
  const panelsArray = panelsMapToSortedArray(panels);
1180
1214
  const index = panelsArray.indexOf(panel);
1181
1215
  if (index < 0) {
@@ -1185,8 +1219,8 @@ function PanelGroupWithForwardedRef({
1185
1219
  if (currentSize === nextSize) {
1186
1220
  return;
1187
1221
  }
1188
- if (panel.current.collapsible && nextSize === 0) ; else {
1189
- nextSize = Math.min(panel.current.maxSize, Math.max(panel.current.minSize, nextSize));
1222
+ if (collapsible && nextSize === collapsedSize) ; else {
1223
+ nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1190
1224
  }
1191
1225
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1192
1226
  if (idBefore == null || idAfter == null) {
@@ -1197,10 +1231,11 @@ function PanelGroupWithForwardedRef({
1197
1231
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1198
1232
  if (prevSizes !== nextSizes) {
1199
1233
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1234
+ setSizes(nextSizes);
1200
1235
 
1201
1236
  // If resize change handlers have been declared, this is the time to call them.
1237
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1202
1238
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1203
- setSizes(nextSizes);
1204
1239
  }
1205
1240
  }, []);
1206
1241
  const context = useMemo(() => ({
@@ -69,6 +69,7 @@ PanelGroupContext.displayName = "PanelGroupContext";
69
69
  function PanelWithForwardedRef({
70
70
  children = null,
71
71
  className: classNameFromProps = "",
72
+ collapsedSize = 0,
72
73
  collapsible = false,
73
74
  defaultSize = null,
74
75
  forwardedRef,
@@ -126,6 +127,7 @@ function PanelWithForwardedRef({
126
127
  });
127
128
  const panelDataRef = useRef({
128
129
  callbacksRef,
130
+ collapsedSize,
129
131
  collapsible,
130
132
  defaultSize,
131
133
  id: panelId,
@@ -136,6 +138,7 @@ function PanelWithForwardedRef({
136
138
  useIsomorphicLayoutEffect(() => {
137
139
  committedValuesRef.current.size = parseSizeFromStyle(style);
138
140
  panelDataRef.current.callbacksRef = callbacksRef;
141
+ panelDataRef.current.collapsedSize = collapsedSize;
139
142
  panelDataRef.current.collapsible = collapsible;
140
143
  panelDataRef.current.defaultSize = defaultSize;
141
144
  panelDataRef.current.id = panelId;
@@ -283,11 +286,18 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
283
286
  }
284
287
  function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
285
288
  sizes.forEach((size, index) => {
289
+ const panelRef = panelsArray[index];
290
+ if (!panelRef) {
291
+ // Handle initial mount (when panels are registered too late to be in the panels array)
292
+ // The subsequent render+effects will handle the resize notification
293
+ return;
294
+ }
286
295
  const {
287
296
  callbacksRef,
297
+ collapsedSize,
288
298
  collapsible,
289
299
  id
290
- } = panelsArray[index].current;
300
+ } = panelRef.current;
291
301
  const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
292
302
  if (lastNotifiedSize !== size) {
293
303
  panelIdToLastNotifiedSizeMap[id] = size;
@@ -296,14 +306,12 @@ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
296
306
  onResize
297
307
  } = callbacksRef.current;
298
308
  if (onResize) {
299
- onResize(size);
309
+ onResize(size, lastNotifiedSize);
300
310
  }
301
311
  if (collapsible && onCollapse) {
302
- // Falsy check handles both previous size of 0
303
- // and initial size of undefined (when mounting)
304
- if (!lastNotifiedSize && size !== 0) {
312
+ if ((lastNotifiedSize == null || lastNotifiedSize === collapsedSize) && size !== collapsedSize) {
305
313
  onCollapse(false);
306
- } else if (lastNotifiedSize !== 0 && size === 0) {
314
+ } else if (lastNotifiedSize !== collapsedSize && size === collapsedSize) {
307
315
  onCollapse(true);
308
316
  }
309
317
  }
@@ -395,11 +403,17 @@ function panelsMapToSortedArray(panels) {
395
403
  }
396
404
  function safeResizePanel(panel, delta, prevSize, event) {
397
405
  const nextSizeUnsafe = prevSize + delta;
398
- if (panel.current.collapsible) {
399
- if (prevSize > 0) {
406
+ const {
407
+ collapsedSize,
408
+ collapsible,
409
+ maxSize,
410
+ minSize
411
+ } = panel.current;
412
+ if (collapsible) {
413
+ if (prevSize > collapsedSize) {
400
414
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
401
- if (nextSizeUnsafe <= panel.current.minSize / 2) {
402
- return 0;
415
+ if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
416
+ return collapsedSize;
403
417
  }
404
418
  } else {
405
419
  const isKeyboardEvent = event?.type?.startsWith("key");
@@ -407,13 +421,13 @@ function safeResizePanel(panel, delta, prevSize, event) {
407
421
  // Keyboard events should expand a collapsed panel to the min size,
408
422
  // but mouse events should wait until the panel has reached its min size
409
423
  // to avoid a visual flickering when dragging between collapsed and min size.
410
- if (nextSizeUnsafe < panel.current.minSize) {
411
- return 0;
424
+ if (nextSizeUnsafe < minSize) {
425
+ return collapsedSize;
412
426
  }
413
427
  }
414
428
  }
415
429
  }
416
- const nextSize = Math.min(panel.current.maxSize, Math.max(panel.current.minSize, nextSizeUnsafe));
430
+ const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
417
431
  return nextSize;
418
432
  }
419
433
 
@@ -876,8 +890,8 @@ function PanelGroupWithForwardedRef({
876
890
  } = committedValuesRef.current;
877
891
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
878
892
  const panelsArray = panelsMapToSortedArray(panels);
879
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
880
893
  setSizes(sizes);
894
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
881
895
  }
882
896
  }), []);
883
897
  useIsomorphicLayoutEffect(() => {
@@ -899,25 +913,25 @@ function PanelGroupWithForwardedRef({
899
913
  const {
900
914
  onLayout
901
915
  } = callbacksRef.current;
902
- if (onLayout) {
903
- const {
904
- panels,
905
- sizes
906
- } = committedValuesRef.current;
916
+ const {
917
+ panels,
918
+ sizes
919
+ } = committedValuesRef.current;
907
920
 
908
- // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
909
- if (sizes.length > 0) {
921
+ // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
922
+ if (sizes.length > 0) {
923
+ if (onLayout) {
910
924
  onLayout(sizes);
911
- const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
912
-
913
- // When possible, we notify before the next render so that rendering work can be batched together.
914
- // Some cases are difficult to detect though,
915
- // for example– panels that are conditionally rendered can affect the size of neighboring panels.
916
- // In this case, the best we can do is notify on commit.
917
- // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
918
- const panelsArray = panelsMapToSortedArray(panels);
919
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
920
925
  }
926
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
927
+
928
+ // When possible, we notify before the next render so that rendering work can be batched together.
929
+ // Some cases are difficult to detect though,
930
+ // for example– panels that are conditionally rendered can affect the size of neighboring panels.
931
+ // In this case, the best we can do is notify on commit.
932
+ // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
933
+ const panelsArray = panelsMapToSortedArray(panels);
934
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
921
935
  }
922
936
  }, [sizes]);
923
937
 
@@ -1087,10 +1101,11 @@ function PanelGroupWithForwardedRef({
1087
1101
  }
1088
1102
  if (sizesChanged) {
1089
1103
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1104
+ setSizes(nextSizes);
1090
1105
 
1091
1106
  // If resize change handlers have been declared, this is the time to call them.
1107
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1092
1108
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1093
- setSizes(nextSizes);
1094
1109
  }
1095
1110
  prevDeltaRef.current = delta;
1096
1111
  };
@@ -1112,7 +1127,14 @@ function PanelGroupWithForwardedRef({
1112
1127
  sizes: prevSizes
1113
1128
  } = committedValuesRef.current;
1114
1129
  const panel = panels.get(id);
1115
- if (panel == null || !panel.current.collapsible) {
1130
+ if (panel == null) {
1131
+ return;
1132
+ }
1133
+ const {
1134
+ collapsedSize,
1135
+ collapsible
1136
+ } = panel.current;
1137
+ if (!collapsible) {
1116
1138
  return;
1117
1139
  }
1118
1140
  const panelsArray = panelsMapToSortedArray(panels);
@@ -1121,7 +1143,7 @@ function PanelGroupWithForwardedRef({
1121
1143
  return;
1122
1144
  }
1123
1145
  const currentSize = prevSizes[index];
1124
- if (currentSize === 0) {
1146
+ if (currentSize === collapsedSize) {
1125
1147
  // Panel is already collapsed.
1126
1148
  return;
1127
1149
  }
@@ -1131,14 +1153,15 @@ function PanelGroupWithForwardedRef({
1131
1153
  return;
1132
1154
  }
1133
1155
  const isLastPanel = index === panelsArray.length - 1;
1134
- const delta = isLastPanel ? currentSize : 0 - currentSize;
1156
+ const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1135
1157
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1136
1158
  if (prevSizes !== nextSizes) {
1137
1159
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1160
+ setSizes(nextSizes);
1138
1161
 
1139
1162
  // If resize change handlers have been declared, this is the time to call them.
1163
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1140
1164
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1141
- setSizes(nextSizes);
1142
1165
  }
1143
1166
  }, []);
1144
1167
  const expandPanel = useCallback(id => {
@@ -1150,7 +1173,11 @@ function PanelGroupWithForwardedRef({
1150
1173
  if (panel == null) {
1151
1174
  return;
1152
1175
  }
1153
- const sizeBeforeCollapse = panelSizeBeforeCollapse.current.get(id) || panel.current.minSize;
1176
+ const {
1177
+ collapsedSize,
1178
+ minSize
1179
+ } = panel.current;
1180
+ const sizeBeforeCollapse = panelSizeBeforeCollapse.current.get(id) || minSize;
1154
1181
  if (!sizeBeforeCollapse) {
1155
1182
  return;
1156
1183
  }
@@ -1160,7 +1187,7 @@ function PanelGroupWithForwardedRef({
1160
1187
  return;
1161
1188
  }
1162
1189
  const currentSize = prevSizes[index];
1163
- if (currentSize !== 0) {
1190
+ if (currentSize !== collapsedSize) {
1164
1191
  // Panel is already expanded.
1165
1192
  return;
1166
1193
  }
@@ -1169,14 +1196,15 @@ function PanelGroupWithForwardedRef({
1169
1196
  return;
1170
1197
  }
1171
1198
  const isLastPanel = index === panelsArray.length - 1;
1172
- const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
1199
+ const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1173
1200
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1174
1201
  if (prevSizes !== nextSizes) {
1175
1202
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1203
+ setSizes(nextSizes);
1176
1204
 
1177
1205
  // If resize change handlers have been declared, this is the time to call them.
1206
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1178
1207
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1179
- setSizes(nextSizes);
1180
1208
  }
1181
1209
  }, []);
1182
1210
  const resizePanel = useCallback((id, nextSize) => {
@@ -1188,6 +1216,12 @@ function PanelGroupWithForwardedRef({
1188
1216
  if (panel == null) {
1189
1217
  return;
1190
1218
  }
1219
+ const {
1220
+ collapsedSize,
1221
+ collapsible,
1222
+ maxSize,
1223
+ minSize
1224
+ } = panel.current;
1191
1225
  const panelsArray = panelsMapToSortedArray(panels);
1192
1226
  const index = panelsArray.indexOf(panel);
1193
1227
  if (index < 0) {
@@ -1197,8 +1231,8 @@ function PanelGroupWithForwardedRef({
1197
1231
  if (currentSize === nextSize) {
1198
1232
  return;
1199
1233
  }
1200
- if (panel.current.collapsible && nextSize === 0) ; else {
1201
- nextSize = Math.min(panel.current.maxSize, Math.max(panel.current.minSize, nextSize));
1234
+ if (collapsible && nextSize === collapsedSize) ; else {
1235
+ nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1202
1236
  }
1203
1237
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1204
1238
  if (idBefore == null || idAfter == null) {
@@ -1209,10 +1243,11 @@ function PanelGroupWithForwardedRef({
1209
1243
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1210
1244
  if (prevSizes !== nextSizes) {
1211
1245
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1246
+ setSizes(nextSizes);
1212
1247
 
1213
1248
  // If resize change handlers have been declared, this is the time to call them.
1249
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
1214
1250
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1215
- setSizes(nextSizes);
1216
1251
  }
1217
1252
  }, []);
1218
1253
  const context = useMemo(() => ({