react-resizable-panels 0.0.59 → 0.0.61

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.
@@ -67,6 +67,7 @@ function PanelWithForwardedRef({
67
67
  expandPanel,
68
68
  getPanelSize,
69
69
  getPanelStyle,
70
+ groupId,
70
71
  isPanelCollapsed,
71
72
  registerPanel,
72
73
  resizePanel,
@@ -132,6 +133,7 @@ function PanelWithForwardedRef({
132
133
  // CSS selectors
133
134
  "data-panel": "",
134
135
  "data-panel-id": panelId,
136
+ "data-panel-group-id": groupId,
135
137
  // e2e test attributes
136
138
  "data-panel-collapsible": undefined,
137
139
  "data-panel-size": undefined
@@ -606,6 +608,7 @@ function getResizeHandlePanelIds(groupId, handleId, panelsArray) {
606
608
 
607
609
  function useWindowSplitterPanelGroupBehavior({
608
610
  committedValuesRef,
611
+ eagerValuesRef,
609
612
  groupId,
610
613
  layout,
611
614
  panelDataArray,
@@ -617,7 +620,7 @@ function useWindowSplitterPanelGroupBehavior({
617
620
  useEffect(() => {
618
621
  const {
619
622
  panelDataArray
620
- } = committedValuesRef.current;
623
+ } = eagerValuesRef.current;
621
624
  const groupElement = getPanelGroupElement(groupId);
622
625
  assert(groupElement != null, `No group found for id "${groupId}"`);
623
626
  const handles = getResizeHandleElementsForGroup(groupId);
@@ -675,7 +678,7 @@ function useWindowSplitterPanelGroupBehavior({
675
678
  return () => {
676
679
  cleanupFunctions.forEach(cleanupFunction => cleanupFunction());
677
680
  };
678
- }, [committedValuesRef, groupId, layout, panelDataArray, setLayout]);
681
+ }, [committedValuesRef, eagerValuesRef, groupId, layout, panelDataArray, setLayout]);
679
682
  }
680
683
 
681
684
  function areEqual(arrayA, arrayB) {
@@ -772,6 +775,44 @@ function calculateDeltaPercentage(event, groupId, dragHandleId, direction, initi
772
775
  }
773
776
  }
774
777
 
778
+ function calculateUnsafeDefaultLayout({
779
+ groupSizePixels,
780
+ panelDataArray
781
+ }) {
782
+ const layout = Array(panelDataArray.length);
783
+ const panelDataConstraints = panelDataArray.map(panelData => panelData.constraints);
784
+ let numPanelsWithSizes = 0;
785
+ let remainingSize = 100;
786
+
787
+ // Distribute default sizes first
788
+ for (let index = 0; index < panelDataArray.length; index++) {
789
+ const {
790
+ defaultSizePercentage
791
+ } = computePercentagePanelConstraints(panelDataConstraints, index, groupSizePixels);
792
+ if (defaultSizePercentage != null) {
793
+ numPanelsWithSizes++;
794
+ layout[index] = defaultSizePercentage;
795
+ remainingSize -= defaultSizePercentage;
796
+ }
797
+ }
798
+
799
+ // Remaining size should be distributed evenly between panels without default sizes
800
+ for (let index = 0; index < panelDataArray.length; index++) {
801
+ const {
802
+ defaultSizePercentage
803
+ } = computePercentagePanelConstraints(panelDataConstraints, index, groupSizePixels);
804
+ if (defaultSizePercentage != null) {
805
+ continue;
806
+ }
807
+ const numRemainingPanels = panelDataArray.length - numPanelsWithSizes;
808
+ const size = remainingSize / numRemainingPanels;
809
+ numPanelsWithSizes++;
810
+ layout[index] = size;
811
+ remainingSize -= size;
812
+ }
813
+ return layout;
814
+ }
815
+
775
816
  function convertPercentageToPixels(percentage, groupSizePixels) {
776
817
  return percentage / 100 * groupSizePixels;
777
818
  }
@@ -922,6 +963,10 @@ function debounce(callback, durationMs = 10) {
922
963
  return callable;
923
964
  }
924
965
 
966
+ function getPanelElementsForGroup(groupId) {
967
+ return Array.from(document.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
968
+ }
969
+
925
970
  // PanelGroup might be rendering in a server-side environment where localStorage is not available
926
971
  // or on a browser with cookies/storage disabled.
927
972
  // In either case, this function avoids accessing localStorage until needed,
@@ -977,6 +1022,15 @@ function loadSerializedPanelGroupState(autoSaveId, storage) {
977
1022
  } catch (error) {}
978
1023
  return null;
979
1024
  }
1025
+ function loadPanelLayout(autoSaveId, panels, storage) {
1026
+ const state = loadSerializedPanelGroupState(autoSaveId, storage);
1027
+ if (state) {
1028
+ var _state$key;
1029
+ const key = getSerializationKey(panels);
1030
+ return (_state$key = state[key]) !== null && _state$key !== void 0 ? _state$key : null;
1031
+ }
1032
+ return null;
1033
+ }
980
1034
  function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
981
1035
  const key = getSerializationKey(panels);
982
1036
  const state = loadSerializedPanelGroupState(autoSaveId, storage) || {};
@@ -988,6 +1042,12 @@ function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
988
1042
  }
989
1043
  }
990
1044
 
1045
+ function shouldMonitorPixelBasedConstraints(constraints) {
1046
+ return constraints.some(constraints => {
1047
+ return constraints.collapsedSizePixels !== undefined || constraints.maxSizePixels !== undefined || constraints.minSizePixels !== undefined;
1048
+ });
1049
+ }
1050
+
991
1051
  // All units must be in percentages; pixel values should be pre-converted
992
1052
  function validatePanelGroupLayout({
993
1053
  groupSizePixels,
@@ -1056,7 +1116,7 @@ const defaultStorage = {
1056
1116
  };
1057
1117
  const debounceMap = {};
1058
1118
  function PanelGroupWithForwardedRef({
1059
- autoSaveId,
1119
+ autoSaveId = null,
1060
1120
  children,
1061
1121
  className: classNameFromProps = "",
1062
1122
  dataAttributes,
@@ -1073,20 +1133,22 @@ function PanelGroupWithForwardedRef({
1073
1133
  const groupId = useUniqueId(idFromProps);
1074
1134
  const [dragState, setDragState] = useState(null);
1075
1135
  const [layout, setLayout] = useState([]);
1076
- const [panelDataArray, setPanelDataArray] = useState([]);
1077
1136
  const panelIdToLastNotifiedMixedSizesMapRef = useRef({});
1078
1137
  const panelSizeBeforeCollapseRef = useRef(new Map());
1079
1138
  const prevDeltaRef = useRef(0);
1080
- const [imperativeApiQueue, setImperativeApiQueue] = useState([]);
1081
1139
  const committedValuesRef = useRef({
1140
+ autoSaveId,
1082
1141
  direction,
1083
1142
  dragState,
1084
1143
  id: groupId,
1085
1144
  keyboardResizeByPercentage,
1086
1145
  keyboardResizeByPixels,
1087
- layout,
1088
1146
  onLayout,
1089
- panelDataArray
1147
+ storage
1148
+ });
1149
+ const eagerValuesRef = useRef({
1150
+ layout,
1151
+ panelDataArray: []
1090
1152
  });
1091
1153
  useRef({
1092
1154
  didLogIdAndOrderWarning: false,
@@ -1097,9 +1159,11 @@ function PanelGroupWithForwardedRef({
1097
1159
  getId: () => committedValuesRef.current.id,
1098
1160
  getLayout: () => {
1099
1161
  const {
1100
- id: groupId,
1101
- layout
1162
+ id: groupId
1102
1163
  } = committedValuesRef.current;
1164
+ const {
1165
+ layout
1166
+ } = eagerValuesRef.current;
1103
1167
  const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1104
1168
  return layout.map(sizePercentage => {
1105
1169
  return {
@@ -1111,10 +1175,12 @@ function PanelGroupWithForwardedRef({
1111
1175
  setLayout: mixedSizes => {
1112
1176
  const {
1113
1177
  id: groupId,
1178
+ onLayout
1179
+ } = committedValuesRef.current;
1180
+ const {
1114
1181
  layout: prevLayout,
1115
- onLayout,
1116
1182
  panelDataArray
1117
- } = committedValuesRef.current;
1183
+ } = eagerValuesRef.current;
1118
1184
  const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1119
1185
  const unsafeLayout = mixedSizes.map(mixedSize => getPercentageSizeFromMixedSizes(mixedSize, groupSizePixels));
1120
1186
  const safeLayout = validatePanelGroupLayout({
@@ -1124,6 +1190,7 @@ function PanelGroupWithForwardedRef({
1124
1190
  });
1125
1191
  if (!areEqual(prevLayout, safeLayout)) {
1126
1192
  setLayout(safeLayout);
1193
+ eagerValuesRef.current.layout = safeLayout;
1127
1194
  if (onLayout) {
1128
1195
  onLayout(safeLayout.map(sizePercentage => ({
1129
1196
  sizePercentage,
@@ -1134,14 +1201,20 @@ function PanelGroupWithForwardedRef({
1134
1201
  }
1135
1202
  }
1136
1203
  }), []);
1204
+
1137
1205
  useWindowSplitterPanelGroupBehavior({
1138
1206
  committedValuesRef,
1207
+ eagerValuesRef,
1139
1208
  groupId,
1140
1209
  layout,
1141
- panelDataArray,
1210
+ panelDataArray: eagerValuesRef.current.panelDataArray,
1142
1211
  setLayout
1143
1212
  });
1144
1213
  useEffect(() => {
1214
+ const {
1215
+ panelDataArray
1216
+ } = eagerValuesRef.current;
1217
+
1145
1218
  // If this panel has been configured to persist sizing information, save sizes to local storage.
1146
1219
  if (autoSaveId) {
1147
1220
  if (layout.length === 0 || layout.length !== panelDataArray.length) {
@@ -1154,7 +1227,7 @@ function PanelGroupWithForwardedRef({
1154
1227
  }
1155
1228
  debounceMap[autoSaveId](autoSaveId, panelDataArray, layout, storage);
1156
1229
  }
1157
- }, [autoSaveId, layout, panelDataArray, storage]);
1230
+ }, [autoSaveId, layout, storage]);
1158
1231
 
1159
1232
  // DEV warnings
1160
1233
  useEffect(() => {
@@ -1162,22 +1235,13 @@ function PanelGroupWithForwardedRef({
1162
1235
 
1163
1236
  // External APIs are safe to memoize via committed values ref
1164
1237
  const collapsePanel = useCallback(panelData => {
1238
+ const {
1239
+ onLayout
1240
+ } = committedValuesRef.current;
1165
1241
  const {
1166
1242
  layout: prevLayout,
1167
- onLayout,
1168
1243
  panelDataArray
1169
- } = committedValuesRef.current;
1170
-
1171
- // See issues/211
1172
- if (panelDataArray.find(({
1173
- id
1174
- }) => id === panelData.id) == null) {
1175
- setImperativeApiQueue(prev => [...prev, {
1176
- panelData,
1177
- type: "collapse"
1178
- }]);
1179
- return;
1180
- }
1244
+ } = eagerValuesRef.current;
1181
1245
  if (panelData.constraints.collapsible) {
1182
1246
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1183
1247
  const {
@@ -1202,6 +1266,7 @@ function PanelGroupWithForwardedRef({
1202
1266
  });
1203
1267
  if (!compareLayouts(prevLayout, nextLayout)) {
1204
1268
  setLayout(nextLayout);
1269
+ eagerValuesRef.current.layout = nextLayout;
1205
1270
  if (onLayout) {
1206
1271
  onLayout(nextLayout.map(sizePercentage => ({
1207
1272
  sizePercentage,
@@ -1216,22 +1281,13 @@ function PanelGroupWithForwardedRef({
1216
1281
 
1217
1282
  // External APIs are safe to memoize via committed values ref
1218
1283
  const expandPanel = useCallback(panelData => {
1284
+ const {
1285
+ onLayout
1286
+ } = committedValuesRef.current;
1219
1287
  const {
1220
1288
  layout: prevLayout,
1221
- onLayout,
1222
1289
  panelDataArray
1223
- } = committedValuesRef.current;
1224
-
1225
- // See issues/211
1226
- if (panelDataArray.find(({
1227
- id
1228
- }) => id === panelData.id) == null) {
1229
- setImperativeApiQueue(prev => [...prev, {
1230
- panelData,
1231
- type: "expand"
1232
- }]);
1233
- return;
1234
- }
1290
+ } = eagerValuesRef.current;
1235
1291
  if (panelData.constraints.collapsible) {
1236
1292
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1237
1293
  const {
@@ -1257,6 +1313,7 @@ function PanelGroupWithForwardedRef({
1257
1313
  });
1258
1314
  if (!compareLayouts(prevLayout, nextLayout)) {
1259
1315
  setLayout(nextLayout);
1316
+ eagerValuesRef.current.layout = nextLayout;
1260
1317
  if (onLayout) {
1261
1318
  onLayout(nextLayout.map(sizePercentage => ({
1262
1319
  sizePercentage,
@@ -1274,7 +1331,7 @@ function PanelGroupWithForwardedRef({
1274
1331
  const {
1275
1332
  layout,
1276
1333
  panelDataArray
1277
- } = committedValuesRef.current;
1334
+ } = eagerValuesRef.current;
1278
1335
  const {
1279
1336
  panelSizePercentage,
1280
1337
  panelSizePixels
@@ -1287,6 +1344,9 @@ function PanelGroupWithForwardedRef({
1287
1344
 
1288
1345
  // This API should never read from committedValuesRef
1289
1346
  const getPanelStyle = useCallback(panelData => {
1347
+ const {
1348
+ panelDataArray
1349
+ } = eagerValuesRef.current;
1290
1350
  const panelIndex = panelDataArray.indexOf(panelData);
1291
1351
  return computePanelFlexBoxStyle({
1292
1352
  dragState,
@@ -1294,14 +1354,14 @@ function PanelGroupWithForwardedRef({
1294
1354
  panelData: panelDataArray,
1295
1355
  panelIndex
1296
1356
  });
1297
- }, [dragState, layout, panelDataArray]);
1357
+ }, [dragState, layout]);
1298
1358
 
1299
1359
  // External APIs are safe to memoize via committed values ref
1300
1360
  const isPanelCollapsed = useCallback(panelData => {
1301
1361
  const {
1302
1362
  layout,
1303
1363
  panelDataArray
1304
- } = committedValuesRef.current;
1364
+ } = eagerValuesRef.current;
1305
1365
  const {
1306
1366
  collapsedSizePercentage,
1307
1367
  collapsible,
@@ -1315,7 +1375,7 @@ function PanelGroupWithForwardedRef({
1315
1375
  const {
1316
1376
  layout,
1317
1377
  panelDataArray
1318
- } = committedValuesRef.current;
1378
+ } = eagerValuesRef.current;
1319
1379
  const {
1320
1380
  collapsedSizePercentage,
1321
1381
  collapsible,
@@ -1324,22 +1384,82 @@ function PanelGroupWithForwardedRef({
1324
1384
  return !collapsible || panelSizePercentage > collapsedSizePercentage;
1325
1385
  }, [groupId]);
1326
1386
  const registerPanel = useCallback(panelData => {
1327
- setPanelDataArray(prevPanelDataArray => {
1328
- const nextPanelDataArray = [...prevPanelDataArray, panelData];
1329
- return nextPanelDataArray.sort((panelA, panelB) => {
1330
- const orderA = panelA.order;
1331
- const orderB = panelB.order;
1332
- if (orderA == null && orderB == null) {
1333
- return 0;
1334
- } else if (orderA == null) {
1335
- return -1;
1336
- } else if (orderB == null) {
1337
- return 1;
1338
- } else {
1339
- return orderA - orderB;
1340
- }
1387
+ const {
1388
+ autoSaveId,
1389
+ id: groupId,
1390
+ onLayout,
1391
+ storage
1392
+ } = committedValuesRef.current;
1393
+ const {
1394
+ layout: prevLayout,
1395
+ panelDataArray
1396
+ } = eagerValuesRef.current;
1397
+ panelDataArray.push(panelData);
1398
+ panelDataArray.sort((panelA, panelB) => {
1399
+ const orderA = panelA.order;
1400
+ const orderB = panelB.order;
1401
+ if (orderA == null && orderB == null) {
1402
+ return 0;
1403
+ } else if (orderA == null) {
1404
+ return -1;
1405
+ } else if (orderB == null) {
1406
+ return 1;
1407
+ } else {
1408
+ return orderA - orderB;
1409
+ }
1410
+ });
1411
+
1412
+ // Wait until all panels have registered before we try to compute layout;
1413
+ // doing it earlier is both wasteful and may trigger misleading warnings in development mode.
1414
+ const panelElements = getPanelElementsForGroup(groupId);
1415
+ if (panelElements.length !== panelDataArray.length) {
1416
+ return;
1417
+ }
1418
+
1419
+ // If this panel has been configured to persist sizing information,
1420
+ // default size should be restored from local storage if possible.
1421
+ let unsafeLayout = null;
1422
+ if (autoSaveId) {
1423
+ unsafeLayout = loadPanelLayout(autoSaveId, panelDataArray, storage);
1424
+ }
1425
+ const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1426
+ if (groupSizePixels <= 0) {
1427
+ if (shouldMonitorPixelBasedConstraints(panelDataArray.map(({
1428
+ constraints
1429
+ }) => constraints))) {
1430
+ // Wait until the group has rendered a non-zero size before computing layout.
1431
+ return;
1432
+ }
1433
+ }
1434
+ if (unsafeLayout == null) {
1435
+ unsafeLayout = calculateUnsafeDefaultLayout({
1436
+ groupSizePixels,
1437
+ panelDataArray
1341
1438
  });
1439
+ }
1440
+
1441
+ // Validate even saved layouts in case something has changed since last render
1442
+ // e.g. for pixel groups, this could be the size of the window
1443
+ const nextLayout = validatePanelGroupLayout({
1444
+ groupSizePixels,
1445
+ layout: unsafeLayout,
1446
+ panelConstraints: panelDataArray.map(panelData => panelData.constraints)
1342
1447
  });
1448
+
1449
+ // Offscreen mode makes this a bit weird;
1450
+ // Panels unregister when hidden and re-register when shown again,
1451
+ // but the overall layout doesn't change between these two cases.
1452
+ setLayout(nextLayout);
1453
+ eagerValuesRef.current.layout = nextLayout;
1454
+ if (!areEqual(prevLayout, nextLayout)) {
1455
+ if (onLayout) {
1456
+ onLayout(nextLayout.map(sizePercentage => ({
1457
+ sizePercentage,
1458
+ sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
1459
+ })));
1460
+ }
1461
+ callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
1462
+ }
1343
1463
  }, []);
1344
1464
  const registerResizeHandle = useCallback(dragHandleId => {
1345
1465
  return function resizeHandler(event) {
@@ -1350,10 +1470,12 @@ function PanelGroupWithForwardedRef({
1350
1470
  id: groupId,
1351
1471
  keyboardResizeByPercentage,
1352
1472
  keyboardResizeByPixels,
1353
- onLayout,
1354
- panelDataArray,
1355
- layout: prevLayout
1473
+ onLayout
1356
1474
  } = committedValuesRef.current;
1475
+ const {
1476
+ layout: prevLayout,
1477
+ panelDataArray
1478
+ } = eagerValuesRef.current;
1357
1479
  const {
1358
1480
  initialLayout
1359
1481
  } = dragState !== null && dragState !== void 0 ? dragState : {};
@@ -1409,6 +1531,7 @@ function PanelGroupWithForwardedRef({
1409
1531
  }
1410
1532
  if (layoutChanged) {
1411
1533
  setLayout(nextLayout);
1534
+ eagerValuesRef.current.layout = nextLayout;
1412
1535
  if (onLayout) {
1413
1536
  onLayout(nextLayout.map(sizePercentage => ({
1414
1537
  sizePercentage,
@@ -1422,23 +1545,13 @@ function PanelGroupWithForwardedRef({
1422
1545
 
1423
1546
  // External APIs are safe to memoize via committed values ref
1424
1547
  const resizePanel = useCallback((panelData, mixedSizes) => {
1548
+ const {
1549
+ onLayout
1550
+ } = committedValuesRef.current;
1425
1551
  const {
1426
1552
  layout: prevLayout,
1427
- onLayout,
1428
1553
  panelDataArray
1429
- } = committedValuesRef.current;
1430
-
1431
- // See issues/211
1432
- if (panelDataArray.find(({
1433
- id
1434
- }) => id === panelData.id) == null) {
1435
- setImperativeApiQueue(prev => [...prev, {
1436
- panelData,
1437
- mixedSizes,
1438
- type: "resize"
1439
- }]);
1440
- return;
1441
- }
1554
+ } = eagerValuesRef.current;
1442
1555
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1443
1556
  const {
1444
1557
  groupSizePixels,
@@ -1458,6 +1571,7 @@ function PanelGroupWithForwardedRef({
1458
1571
  });
1459
1572
  if (!compareLayouts(prevLayout, nextLayout)) {
1460
1573
  setLayout(nextLayout);
1574
+ eagerValuesRef.current.layout = nextLayout;
1461
1575
  if (onLayout) {
1462
1576
  onLayout(nextLayout.map(sizePercentage => ({
1463
1577
  sizePercentage,
@@ -1469,9 +1583,11 @@ function PanelGroupWithForwardedRef({
1469
1583
  }, [groupId]);
1470
1584
  const startDragging = useCallback((dragHandleId, event) => {
1471
1585
  const {
1472
- direction,
1473
- layout
1586
+ direction
1474
1587
  } = committedValuesRef.current;
1588
+ const {
1589
+ layout
1590
+ } = eagerValuesRef.current;
1475
1591
  const handleElement = getResizeHandleElement(dragHandleId);
1476
1592
  const initialCursorPosition = getResizeEventCursorPosition(direction, event);
1477
1593
  setDragState({
@@ -1485,16 +1601,86 @@ function PanelGroupWithForwardedRef({
1485
1601
  resetGlobalCursorStyle();
1486
1602
  setDragState(null);
1487
1603
  }, []);
1604
+ const unregisterPanelRef = useRef({
1605
+ pendingPanelIds: new Set(),
1606
+ timeout: null
1607
+ });
1488
1608
  const unregisterPanel = useCallback(panelData => {
1489
- delete panelIdToLastNotifiedMixedSizesMapRef.current[panelData.id];
1490
- setPanelDataArray(panelDataArray => {
1491
- const index = panelDataArray.indexOf(panelData);
1492
- if (index >= 0) {
1493
- panelDataArray = [...panelDataArray];
1494
- panelDataArray.splice(index, 1);
1609
+ const {
1610
+ id: groupId,
1611
+ onLayout
1612
+ } = committedValuesRef.current;
1613
+ const {
1614
+ layout: prevLayout,
1615
+ panelDataArray
1616
+ } = eagerValuesRef.current;
1617
+ const index = panelDataArray.indexOf(panelData);
1618
+ if (index >= 0) {
1619
+ panelDataArray.splice(index, 1);
1620
+ unregisterPanelRef.current.pendingPanelIds.add(panelData.id);
1621
+ }
1622
+ if (unregisterPanelRef.current.timeout != null) {
1623
+ clearTimeout(unregisterPanelRef.current.timeout);
1624
+ }
1625
+
1626
+ // Batch panel unmounts so that we only calculate layout once;
1627
+ // This is more efficient and avoids misleading warnings in development mode.
1628
+ // We can't check the DOM to detect this because Panel elements have not yet been removed.
1629
+ unregisterPanelRef.current.timeout = setTimeout(() => {
1630
+ const {
1631
+ pendingPanelIds
1632
+ } = unregisterPanelRef.current;
1633
+ const map = panelIdToLastNotifiedMixedSizesMapRef.current;
1634
+
1635
+ // TRICKY
1636
+ // Strict effects mode
1637
+ let unmountDueToStrictMode = false;
1638
+ pendingPanelIds.forEach(panelId => {
1639
+ pendingPanelIds.delete(panelId);
1640
+ if (panelDataArray.find(({
1641
+ id
1642
+ }) => id === panelId) == null) {
1643
+ unmountDueToStrictMode = true;
1644
+
1645
+ // TRICKY
1646
+ // When a panel is removed from the group, we should delete the most recent prev-size entry for it.
1647
+ // If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
1648
+ // Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
1649
+ delete map[panelData.id];
1650
+ }
1651
+ });
1652
+ if (!unmountDueToStrictMode) {
1653
+ return;
1495
1654
  }
1496
- return panelDataArray;
1497
- });
1655
+ if (panelDataArray.length === 0) {
1656
+ // The group is unmounting; skip layout calculation.
1657
+ return;
1658
+ }
1659
+ const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1660
+ let unsafeLayout = calculateUnsafeDefaultLayout({
1661
+ groupSizePixels,
1662
+ panelDataArray
1663
+ });
1664
+
1665
+ // Validate even saved layouts in case something has changed since last render
1666
+ // e.g. for pixel groups, this could be the size of the window
1667
+ const nextLayout = validatePanelGroupLayout({
1668
+ groupSizePixels,
1669
+ layout: unsafeLayout,
1670
+ panelConstraints: panelDataArray.map(panelData => panelData.constraints)
1671
+ });
1672
+ if (!areEqual(prevLayout, nextLayout)) {
1673
+ setLayout(nextLayout);
1674
+ eagerValuesRef.current.layout = nextLayout;
1675
+ if (onLayout) {
1676
+ onLayout(nextLayout.map(sizePercentage => ({
1677
+ sizePercentage,
1678
+ sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
1679
+ })));
1680
+ }
1681
+ callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
1682
+ }
1683
+ }, 0);
1498
1684
  }, []);
1499
1685
  const context = useMemo(() => ({
1500
1686
  collapsePanel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
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/Panel.ts CHANGED
@@ -115,6 +115,7 @@ export function PanelWithForwardedRef({
115
115
  expandPanel,
116
116
  getPanelSize,
117
117
  getPanelStyle,
118
+ groupId,
118
119
  isPanelCollapsed,
119
120
  registerPanel,
120
121
  resizePanel,
@@ -250,6 +251,7 @@ export function PanelWithForwardedRef({
250
251
  // CSS selectors
251
252
  "data-panel": "",
252
253
  "data-panel-id": panelId,
254
+ "data-panel-group-id": groupId,
253
255
 
254
256
  // e2e test attributes
255
257
  "data-panel-collapsible": isDevelopment