react-resizable-panels 0.0.50 → 0.0.51

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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.51
4
+ * [154](https://github.com/bvaughn/react-resizable-panels/issues/154): `onResize` and `onCollapse` props are called in response to `PanelGroup.setLayout`
5
+ * [123](https://github.com/bvaughn/react-resizable-panels/issues/123): `onResize` called when number of panels in a group change due to conditional rendering
6
+
3
7
  ## 0.0.50
4
8
  * Improved panel size validation in `PanelGroup`.
5
9
 
@@ -281,27 +281,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
281
281
  nextSizes[index] = baseSizes[index] + deltaApplied;
282
282
  return nextSizes;
283
283
  }
284
- function callPanelCallbacks(panelsArray, prevSizes, nextSizes) {
285
- nextSizes.forEach((nextSize, index) => {
286
- const prevSize = prevSizes[index];
287
- if (prevSize !== nextSize) {
288
- const {
289
- callbacksRef,
290
- collapsible
291
- } = panelsArray[index].current;
284
+ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
285
+ sizes.forEach((size, index) => {
286
+ const {
287
+ callbacksRef,
288
+ collapsible,
289
+ id
290
+ } = panelsArray[index].current;
291
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
292
+ if (lastNotifiedSize !== size) {
293
+ panelIdToLastNotifiedSizeMap[id] = size;
292
294
  const {
293
295
  onCollapse,
294
296
  onResize
295
297
  } = callbacksRef.current;
296
298
  if (onResize) {
297
- onResize(nextSize);
299
+ onResize(size);
298
300
  }
299
301
  if (collapsible && onCollapse) {
300
302
  // Falsy check handles both previous size of 0
301
303
  // and initial size of undefined (when mounting)
302
- if (!prevSize && nextSize !== 0) {
304
+ if (!lastNotifiedSize && size !== 0) {
303
305
  onCollapse(false);
304
- } else if (prevSize !== 0 && nextSize === 0) {
306
+ } else if (lastNotifiedSize !== 0 && size === 0) {
305
307
  onCollapse(true);
306
308
  }
307
309
  }
@@ -837,6 +839,7 @@ function PanelGroupWithForwardedRef({
837
839
  useEffect(() => {
838
840
  callbacksRef.current.onLayout = onLayout;
839
841
  });
842
+ const panelIdToLastNotifiedSizeMapRef = useRef({});
840
843
 
841
844
  // 0-1 values representing the relative size of each panel.
842
845
  const [sizes, setSizes] = useState([]);
@@ -861,6 +864,12 @@ function PanelGroupWithForwardedRef({
861
864
  setLayout: sizes => {
862
865
  const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
863
866
  assert(total === 100, "Panel sizes must add up to 100%");
867
+ const {
868
+ panels
869
+ } = committedValuesRef.current;
870
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
871
+ const panelsArray = panelsMapToSortedArray(panels);
872
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
864
873
  setSizes(sizes);
865
874
  }
866
875
  }), []);
@@ -885,31 +894,23 @@ function PanelGroupWithForwardedRef({
885
894
  } = callbacksRef.current;
886
895
  if (onLayout) {
887
896
  const {
897
+ panels,
888
898
  sizes
889
899
  } = committedValuesRef.current;
890
900
 
891
901
  // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
892
902
  if (sizes.length > 0) {
893
903
  onLayout(sizes);
894
- }
895
- }
896
- }, [sizes]);
904
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
897
905
 
898
- // Notify Panel listeners about their initial sizes and collapsed state after mount.
899
- // Subsequent changes will be called by the resizeHandler.
900
- const didNotifyCallbacksAfterMountRef = useRef(false);
901
- useIsomorphicLayoutEffect(() => {
902
- if (didNotifyCallbacksAfterMountRef.current) {
903
- return;
904
- }
905
- const {
906
- panels,
907
- sizes
908
- } = committedValuesRef.current;
909
- if (sizes.length > 0) {
910
- didNotifyCallbacksAfterMountRef.current = true;
911
- const panelsArray = panelsMapToSortedArray(panels);
912
- callPanelCallbacks(panelsArray, [], sizes);
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
+ }
913
914
  }
914
915
  }, [sizes]);
915
916
 
@@ -1073,8 +1074,10 @@ function PanelGroupWithForwardedRef({
1073
1074
  }
1074
1075
  }
1075
1076
  if (sizesChanged) {
1077
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1078
+
1076
1079
  // If resize change handlers have been declared, this is the time to call them.
1077
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1080
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1078
1081
  setSizes(nextSizes);
1079
1082
  }
1080
1083
  prevDeltaRef.current = delta;
@@ -1119,8 +1122,10 @@ function PanelGroupWithForwardedRef({
1119
1122
  const delta = isLastPanel ? currentSize : 0 - currentSize;
1120
1123
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1121
1124
  if (prevSizes !== nextSizes) {
1125
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1126
+
1122
1127
  // If resize change handlers have been declared, this is the time to call them.
1123
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1128
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1124
1129
  setSizes(nextSizes);
1125
1130
  }
1126
1131
  }, []);
@@ -1155,8 +1160,10 @@ function PanelGroupWithForwardedRef({
1155
1160
  const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
1156
1161
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1157
1162
  if (prevSizes !== nextSizes) {
1163
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1164
+
1158
1165
  // If resize change handlers have been declared, this is the time to call them.
1159
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1166
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1160
1167
  setSizes(nextSizes);
1161
1168
  }
1162
1169
  }, []);
@@ -1189,8 +1196,10 @@ function PanelGroupWithForwardedRef({
1189
1196
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1190
1197
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1191
1198
  if (prevSizes !== nextSizes) {
1199
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1200
+
1192
1201
  // If resize change handlers have been declared, this is the time to call them.
1193
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1202
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1194
1203
  setSizes(nextSizes);
1195
1204
  }
1196
1205
  }, []);
@@ -281,27 +281,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
281
281
  nextSizes[index] = baseSizes[index] + deltaApplied;
282
282
  return nextSizes;
283
283
  }
284
- function callPanelCallbacks(panelsArray, prevSizes, nextSizes) {
285
- nextSizes.forEach((nextSize, index) => {
286
- const prevSize = prevSizes[index];
287
- if (prevSize !== nextSize) {
288
- const {
289
- callbacksRef,
290
- collapsible
291
- } = panelsArray[index].current;
284
+ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
285
+ sizes.forEach((size, index) => {
286
+ const {
287
+ callbacksRef,
288
+ collapsible,
289
+ id
290
+ } = panelsArray[index].current;
291
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
292
+ if (lastNotifiedSize !== size) {
293
+ panelIdToLastNotifiedSizeMap[id] = size;
292
294
  const {
293
295
  onCollapse,
294
296
  onResize
295
297
  } = callbacksRef.current;
296
298
  if (onResize) {
297
- onResize(nextSize);
299
+ onResize(size);
298
300
  }
299
301
  if (collapsible && onCollapse) {
300
302
  // Falsy check handles both previous size of 0
301
303
  // and initial size of undefined (when mounting)
302
- if (!prevSize && nextSize !== 0) {
304
+ if (!lastNotifiedSize && size !== 0) {
303
305
  onCollapse(false);
304
- } else if (prevSize !== 0 && nextSize === 0) {
306
+ } else if (lastNotifiedSize !== 0 && size === 0) {
305
307
  onCollapse(true);
306
308
  }
307
309
  }
@@ -844,6 +846,7 @@ function PanelGroupWithForwardedRef({
844
846
  useEffect(() => {
845
847
  callbacksRef.current.onLayout = onLayout;
846
848
  });
849
+ const panelIdToLastNotifiedSizeMapRef = useRef({});
847
850
 
848
851
  // 0-1 values representing the relative size of each panel.
849
852
  const [sizes, setSizes] = useState([]);
@@ -868,6 +871,12 @@ function PanelGroupWithForwardedRef({
868
871
  setLayout: sizes => {
869
872
  const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
870
873
  assert(total === 100, "Panel sizes must add up to 100%");
874
+ const {
875
+ panels
876
+ } = committedValuesRef.current;
877
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
878
+ const panelsArray = panelsMapToSortedArray(panels);
879
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
871
880
  setSizes(sizes);
872
881
  }
873
882
  }), []);
@@ -892,31 +901,23 @@ function PanelGroupWithForwardedRef({
892
901
  } = callbacksRef.current;
893
902
  if (onLayout) {
894
903
  const {
904
+ panels,
895
905
  sizes
896
906
  } = committedValuesRef.current;
897
907
 
898
908
  // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
899
909
  if (sizes.length > 0) {
900
910
  onLayout(sizes);
901
- }
902
- }
903
- }, [sizes]);
911
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
904
912
 
905
- // Notify Panel listeners about their initial sizes and collapsed state after mount.
906
- // Subsequent changes will be called by the resizeHandler.
907
- const didNotifyCallbacksAfterMountRef = useRef(false);
908
- useIsomorphicLayoutEffect(() => {
909
- if (didNotifyCallbacksAfterMountRef.current) {
910
- return;
911
- }
912
- const {
913
- panels,
914
- sizes
915
- } = committedValuesRef.current;
916
- if (sizes.length > 0) {
917
- didNotifyCallbacksAfterMountRef.current = true;
918
- const panelsArray = panelsMapToSortedArray(panels);
919
- callPanelCallbacks(panelsArray, [], sizes);
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
+ }
920
921
  }
921
922
  }, [sizes]);
922
923
 
@@ -1085,8 +1086,10 @@ function PanelGroupWithForwardedRef({
1085
1086
  }
1086
1087
  }
1087
1088
  if (sizesChanged) {
1089
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1090
+
1088
1091
  // If resize change handlers have been declared, this is the time to call them.
1089
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1092
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1090
1093
  setSizes(nextSizes);
1091
1094
  }
1092
1095
  prevDeltaRef.current = delta;
@@ -1131,8 +1134,10 @@ function PanelGroupWithForwardedRef({
1131
1134
  const delta = isLastPanel ? currentSize : 0 - currentSize;
1132
1135
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1133
1136
  if (prevSizes !== nextSizes) {
1137
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1138
+
1134
1139
  // If resize change handlers have been declared, this is the time to call them.
1135
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1140
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1136
1141
  setSizes(nextSizes);
1137
1142
  }
1138
1143
  }, []);
@@ -1167,8 +1172,10 @@ function PanelGroupWithForwardedRef({
1167
1172
  const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
1168
1173
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1169
1174
  if (prevSizes !== nextSizes) {
1175
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1176
+
1170
1177
  // If resize change handlers have been declared, this is the time to call them.
1171
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1178
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1172
1179
  setSizes(nextSizes);
1173
1180
  }
1174
1181
  }, []);
@@ -1201,8 +1208,10 @@ function PanelGroupWithForwardedRef({
1201
1208
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1202
1209
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1203
1210
  if (prevSizes !== nextSizes) {
1211
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1212
+
1204
1213
  // If resize change handlers have been declared, this is the time to call them.
1205
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1214
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1206
1215
  setSizes(nextSizes);
1207
1216
  }
1208
1217
  }, []);
@@ -257,27 +257,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
257
257
  nextSizes[index] = baseSizes[index] + deltaApplied;
258
258
  return nextSizes;
259
259
  }
260
- function callPanelCallbacks(panelsArray, prevSizes, nextSizes) {
261
- nextSizes.forEach((nextSize, index) => {
262
- const prevSize = prevSizes[index];
263
- if (prevSize !== nextSize) {
264
- const {
265
- callbacksRef,
266
- collapsible
267
- } = panelsArray[index].current;
260
+ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
261
+ sizes.forEach((size, index) => {
262
+ const {
263
+ callbacksRef,
264
+ collapsible,
265
+ id
266
+ } = panelsArray[index].current;
267
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
268
+ if (lastNotifiedSize !== size) {
269
+ panelIdToLastNotifiedSizeMap[id] = size;
268
270
  const {
269
271
  onCollapse,
270
272
  onResize
271
273
  } = callbacksRef.current;
272
274
  if (onResize) {
273
- onResize(nextSize);
275
+ onResize(size);
274
276
  }
275
277
  if (collapsible && onCollapse) {
276
278
  // Falsy check handles both previous size of 0
277
279
  // and initial size of undefined (when mounting)
278
- if (!prevSize && nextSize !== 0) {
280
+ if (!lastNotifiedSize && size !== 0) {
279
281
  onCollapse(false);
280
- } else if (prevSize !== 0 && nextSize === 0) {
282
+ } else if (lastNotifiedSize !== 0 && size === 0) {
281
283
  onCollapse(true);
282
284
  }
283
285
  }
@@ -820,6 +822,7 @@ function PanelGroupWithForwardedRef({
820
822
  useEffect(() => {
821
823
  callbacksRef.current.onLayout = onLayout;
822
824
  });
825
+ const panelIdToLastNotifiedSizeMapRef = useRef({});
823
826
 
824
827
  // 0-1 values representing the relative size of each panel.
825
828
  const [sizes, setSizes] = useState([]);
@@ -844,6 +847,12 @@ function PanelGroupWithForwardedRef({
844
847
  setLayout: sizes => {
845
848
  const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
846
849
  assert(total === 100, "Panel sizes must add up to 100%");
850
+ const {
851
+ panels
852
+ } = committedValuesRef.current;
853
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
854
+ const panelsArray = panelsMapToSortedArray(panels);
855
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
847
856
  setSizes(sizes);
848
857
  }
849
858
  }), []);
@@ -868,31 +877,23 @@ function PanelGroupWithForwardedRef({
868
877
  } = callbacksRef.current;
869
878
  if (onLayout) {
870
879
  const {
880
+ panels,
871
881
  sizes
872
882
  } = committedValuesRef.current;
873
883
 
874
884
  // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
875
885
  if (sizes.length > 0) {
876
886
  onLayout(sizes);
877
- }
878
- }
879
- }, [sizes]);
887
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
880
888
 
881
- // Notify Panel listeners about their initial sizes and collapsed state after mount.
882
- // Subsequent changes will be called by the resizeHandler.
883
- const didNotifyCallbacksAfterMountRef = useRef(false);
884
- useIsomorphicLayoutEffect(() => {
885
- if (didNotifyCallbacksAfterMountRef.current) {
886
- return;
887
- }
888
- const {
889
- panels,
890
- sizes
891
- } = committedValuesRef.current;
892
- if (sizes.length > 0) {
893
- didNotifyCallbacksAfterMountRef.current = true;
894
- const panelsArray = panelsMapToSortedArray(panels);
895
- callPanelCallbacks(panelsArray, [], sizes);
889
+ // When possible, we notify before the next render so that rendering work can be batched together.
890
+ // Some cases are difficult to detect though,
891
+ // for example– panels that are conditionally rendered can affect the size of neighboring panels.
892
+ // In this case, the best we can do is notify on commit.
893
+ // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
894
+ const panelsArray = panelsMapToSortedArray(panels);
895
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
896
+ }
896
897
  }
897
898
  }, [sizes]);
898
899
 
@@ -1061,8 +1062,10 @@ function PanelGroupWithForwardedRef({
1061
1062
  }
1062
1063
  }
1063
1064
  if (sizesChanged) {
1065
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1066
+
1064
1067
  // If resize change handlers have been declared, this is the time to call them.
1065
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1068
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1066
1069
  setSizes(nextSizes);
1067
1070
  }
1068
1071
  prevDeltaRef.current = delta;
@@ -1107,8 +1110,10 @@ function PanelGroupWithForwardedRef({
1107
1110
  const delta = isLastPanel ? currentSize : 0 - currentSize;
1108
1111
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1109
1112
  if (prevSizes !== nextSizes) {
1113
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1114
+
1110
1115
  // If resize change handlers have been declared, this is the time to call them.
1111
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1116
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1112
1117
  setSizes(nextSizes);
1113
1118
  }
1114
1119
  }, []);
@@ -1143,8 +1148,10 @@ function PanelGroupWithForwardedRef({
1143
1148
  const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
1144
1149
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1145
1150
  if (prevSizes !== nextSizes) {
1151
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1152
+
1146
1153
  // If resize change handlers have been declared, this is the time to call them.
1147
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1154
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1148
1155
  setSizes(nextSizes);
1149
1156
  }
1150
1157
  }, []);
@@ -1177,8 +1184,10 @@ function PanelGroupWithForwardedRef({
1177
1184
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1178
1185
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1179
1186
  if (prevSizes !== nextSizes) {
1187
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1188
+
1180
1189
  // If resize change handlers have been declared, this is the time to call them.
1181
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1190
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1182
1191
  setSizes(nextSizes);
1183
1192
  }
1184
1193
  }, []);
@@ -257,27 +257,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
257
257
  nextSizes[index] = baseSizes[index] + deltaApplied;
258
258
  return nextSizes;
259
259
  }
260
- function callPanelCallbacks(panelsArray, prevSizes, nextSizes) {
261
- nextSizes.forEach((nextSize, index) => {
262
- const prevSize = prevSizes[index];
263
- if (prevSize !== nextSize) {
264
- const {
265
- callbacksRef,
266
- collapsible
267
- } = panelsArray[index].current;
260
+ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
261
+ sizes.forEach((size, index) => {
262
+ const {
263
+ callbacksRef,
264
+ collapsible,
265
+ id
266
+ } = panelsArray[index].current;
267
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
268
+ if (lastNotifiedSize !== size) {
269
+ panelIdToLastNotifiedSizeMap[id] = size;
268
270
  const {
269
271
  onCollapse,
270
272
  onResize
271
273
  } = callbacksRef.current;
272
274
  if (onResize) {
273
- onResize(nextSize);
275
+ onResize(size);
274
276
  }
275
277
  if (collapsible && onCollapse) {
276
278
  // Falsy check handles both previous size of 0
277
279
  // and initial size of undefined (when mounting)
278
- if (!prevSize && nextSize !== 0) {
280
+ if (!lastNotifiedSize && size !== 0) {
279
281
  onCollapse(false);
280
- } else if (prevSize !== 0 && nextSize === 0) {
282
+ } else if (lastNotifiedSize !== 0 && size === 0) {
281
283
  onCollapse(true);
282
284
  }
283
285
  }
@@ -813,6 +815,7 @@ function PanelGroupWithForwardedRef({
813
815
  useEffect(() => {
814
816
  callbacksRef.current.onLayout = onLayout;
815
817
  });
818
+ const panelIdToLastNotifiedSizeMapRef = useRef({});
816
819
 
817
820
  // 0-1 values representing the relative size of each panel.
818
821
  const [sizes, setSizes] = useState([]);
@@ -837,6 +840,12 @@ function PanelGroupWithForwardedRef({
837
840
  setLayout: sizes => {
838
841
  const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
839
842
  assert(total === 100, "Panel sizes must add up to 100%");
843
+ const {
844
+ panels
845
+ } = committedValuesRef.current;
846
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
847
+ const panelsArray = panelsMapToSortedArray(panels);
848
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
840
849
  setSizes(sizes);
841
850
  }
842
851
  }), []);
@@ -861,31 +870,23 @@ function PanelGroupWithForwardedRef({
861
870
  } = callbacksRef.current;
862
871
  if (onLayout) {
863
872
  const {
873
+ panels,
864
874
  sizes
865
875
  } = committedValuesRef.current;
866
876
 
867
877
  // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
868
878
  if (sizes.length > 0) {
869
879
  onLayout(sizes);
870
- }
871
- }
872
- }, [sizes]);
880
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
873
881
 
874
- // Notify Panel listeners about their initial sizes and collapsed state after mount.
875
- // Subsequent changes will be called by the resizeHandler.
876
- const didNotifyCallbacksAfterMountRef = useRef(false);
877
- useIsomorphicLayoutEffect(() => {
878
- if (didNotifyCallbacksAfterMountRef.current) {
879
- return;
880
- }
881
- const {
882
- panels,
883
- sizes
884
- } = committedValuesRef.current;
885
- if (sizes.length > 0) {
886
- didNotifyCallbacksAfterMountRef.current = true;
887
- const panelsArray = panelsMapToSortedArray(panels);
888
- callPanelCallbacks(panelsArray, [], sizes);
882
+ // When possible, we notify before the next render so that rendering work can be batched together.
883
+ // Some cases are difficult to detect though,
884
+ // for example– panels that are conditionally rendered can affect the size of neighboring panels.
885
+ // In this case, the best we can do is notify on commit.
886
+ // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
887
+ const panelsArray = panelsMapToSortedArray(panels);
888
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
889
+ }
889
890
  }
890
891
  }, [sizes]);
891
892
 
@@ -1049,8 +1050,10 @@ function PanelGroupWithForwardedRef({
1049
1050
  }
1050
1051
  }
1051
1052
  if (sizesChanged) {
1053
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1054
+
1052
1055
  // If resize change handlers have been declared, this is the time to call them.
1053
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1056
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1054
1057
  setSizes(nextSizes);
1055
1058
  }
1056
1059
  prevDeltaRef.current = delta;
@@ -1095,8 +1098,10 @@ function PanelGroupWithForwardedRef({
1095
1098
  const delta = isLastPanel ? currentSize : 0 - currentSize;
1096
1099
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1097
1100
  if (prevSizes !== nextSizes) {
1101
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1102
+
1098
1103
  // If resize change handlers have been declared, this is the time to call them.
1099
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1104
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1100
1105
  setSizes(nextSizes);
1101
1106
  }
1102
1107
  }, []);
@@ -1131,8 +1136,10 @@ function PanelGroupWithForwardedRef({
1131
1136
  const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
1132
1137
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1133
1138
  if (prevSizes !== nextSizes) {
1139
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1140
+
1134
1141
  // If resize change handlers have been declared, this is the time to call them.
1135
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1142
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1136
1143
  setSizes(nextSizes);
1137
1144
  }
1138
1145
  }, []);
@@ -1165,8 +1172,10 @@ function PanelGroupWithForwardedRef({
1165
1172
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1166
1173
  const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1167
1174
  if (prevSizes !== nextSizes) {
1175
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1176
+
1168
1177
  // If resize change handlers have been declared, this is the time to call them.
1169
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
1178
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1170
1179
  setSizes(nextSizes);
1171
1180
  }
1172
1181
  }, []);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "0.0.50",
3
+ "version": "0.0.51",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",
package/src/PanelGroup.ts CHANGED
@@ -169,6 +169,8 @@ function PanelGroupWithForwardedRef({
169
169
  callbacksRef.current.onLayout = onLayout;
170
170
  });
171
171
 
172
+ const panelIdToLastNotifiedSizeMapRef = useRef<Record<string, number>>({});
173
+
172
174
  // 0-1 values representing the relative size of each panel.
173
175
  const [sizes, setSizes] = useState<number[]>([]);
174
176
 
@@ -199,6 +201,13 @@ function PanelGroupWithForwardedRef({
199
201
 
200
202
  assert(total === 100, "Panel sizes must add up to 100%");
201
203
 
204
+ const { panels } = committedValuesRef.current;
205
+ const panelIdToLastNotifiedSizeMap =
206
+ panelIdToLastNotifiedSizeMapRef.current;
207
+ const panelsArray = panelsMapToSortedArray(panels);
208
+
209
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
210
+
202
211
  setSizes(sizes);
203
212
  },
204
213
  }),
@@ -224,29 +233,23 @@ function PanelGroupWithForwardedRef({
224
233
  useEffect(() => {
225
234
  const { onLayout } = callbacksRef.current!;
226
235
  if (onLayout) {
227
- const { sizes } = committedValuesRef.current;
236
+ const { panels, sizes } = committedValuesRef.current;
228
237
 
229
238
  // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
230
239
  if (sizes.length > 0) {
231
240
  onLayout(sizes);
232
- }
233
- }
234
- }, [sizes]);
235
-
236
- // Notify Panel listeners about their initial sizes and collapsed state after mount.
237
- // Subsequent changes will be called by the resizeHandler.
238
- const didNotifyCallbacksAfterMountRef = useRef(false);
239
- useIsomorphicLayoutEffect(() => {
240
- if (didNotifyCallbacksAfterMountRef.current) {
241
- return;
242
- }
243
241
 
244
- const { panels, sizes } = committedValuesRef.current;
245
- if (sizes.length > 0) {
246
- didNotifyCallbacksAfterMountRef.current = true;
242
+ const panelIdToLastNotifiedSizeMap =
243
+ panelIdToLastNotifiedSizeMapRef.current;
247
244
 
248
- const panelsArray = panelsMapToSortedArray(panels);
249
- callPanelCallbacks(panelsArray, [], sizes);
245
+ // When possible, we notify before the next render so that rendering work can be batched together.
246
+ // Some cases are difficult to detect though,
247
+ // for example– panels that are conditionally rendered can affect the size of neighboring panels.
248
+ // In this case, the best we can do is notify on commit.
249
+ // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
250
+ const panelsArray = panelsMapToSortedArray(panels);
251
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
252
+ }
250
253
  }
251
254
  }, [sizes]);
252
255
 
@@ -481,8 +484,15 @@ function PanelGroupWithForwardedRef({
481
484
  }
482
485
 
483
486
  if (sizesChanged) {
487
+ const panelIdToLastNotifiedSizeMap =
488
+ panelIdToLastNotifiedSizeMapRef.current;
489
+
484
490
  // If resize change handlers have been declared, this is the time to call them.
485
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
491
+ callPanelCallbacks(
492
+ panelsArray,
493
+ nextSizes,
494
+ panelIdToLastNotifiedSizeMap
495
+ );
486
496
 
487
497
  setSizes(nextSizes);
488
498
  }
@@ -550,8 +560,11 @@ function PanelGroupWithForwardedRef({
550
560
  null
551
561
  );
552
562
  if (prevSizes !== nextSizes) {
563
+ const panelIdToLastNotifiedSizeMap =
564
+ panelIdToLastNotifiedSizeMapRef.current;
565
+
553
566
  // If resize change handlers have been declared, this is the time to call them.
554
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
567
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
555
568
 
556
569
  setSizes(nextSizes);
557
570
  }
@@ -603,8 +616,11 @@ function PanelGroupWithForwardedRef({
603
616
  null
604
617
  );
605
618
  if (prevSizes !== nextSizes) {
619
+ const panelIdToLastNotifiedSizeMap =
620
+ panelIdToLastNotifiedSizeMapRef.current;
621
+
606
622
  // If resize change handlers have been declared, this is the time to call them.
607
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
623
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
608
624
 
609
625
  setSizes(nextSizes);
610
626
  }
@@ -658,8 +674,11 @@ function PanelGroupWithForwardedRef({
658
674
  null
659
675
  );
660
676
  if (prevSizes !== nextSizes) {
677
+ const panelIdToLastNotifiedSizeMap =
678
+ panelIdToLastNotifiedSizeMapRef.current;
679
+
661
680
  // If resize change handlers have been declared, this is the time to call them.
662
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
681
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
663
682
 
664
683
  setSizes(nextSizes);
665
684
  }
@@ -119,25 +119,28 @@ export function adjustByDelta(
119
119
 
120
120
  export function callPanelCallbacks(
121
121
  panelsArray: PanelData[],
122
- prevSizes: number[],
123
- nextSizes: number[]
122
+ sizes: number[],
123
+ panelIdToLastNotifiedSizeMap: Record<string, number>
124
124
  ) {
125
- nextSizes.forEach((nextSize, index) => {
126
- const prevSize = prevSizes[index];
127
- if (prevSize !== nextSize) {
128
- const { callbacksRef, collapsible } = panelsArray[index].current;
125
+ sizes.forEach((size, index) => {
126
+ const { callbacksRef, collapsible, id } = panelsArray[index].current;
127
+
128
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
129
+ if (lastNotifiedSize !== size) {
130
+ panelIdToLastNotifiedSizeMap[id] = size;
131
+
129
132
  const { onCollapse, onResize } = callbacksRef.current!;
130
133
 
131
134
  if (onResize) {
132
- onResize(nextSize);
135
+ onResize(size);
133
136
  }
134
137
 
135
138
  if (collapsible && onCollapse) {
136
139
  // Falsy check handles both previous size of 0
137
140
  // and initial size of undefined (when mounting)
138
- if (!prevSize && nextSize !== 0) {
141
+ if (!lastNotifiedSize && size !== 0) {
139
142
  onCollapse(false);
140
- } else if (prevSize !== 0 && nextSize === 0) {
143
+ } else if (lastNotifiedSize !== 0 && size === 0) {
141
144
  onCollapse(true);
142
145
  }
143
146
  }