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.
@@ -91,6 +91,7 @@ function PanelWithForwardedRef({
91
91
  expandPanel,
92
92
  getPanelSize,
93
93
  getPanelStyle,
94
+ groupId,
94
95
  isPanelCollapsed,
95
96
  registerPanel,
96
97
  resizePanel,
@@ -156,6 +157,7 @@ function PanelWithForwardedRef({
156
157
  // CSS selectors
157
158
  "data-panel": "",
158
159
  "data-panel-id": panelId,
160
+ "data-panel-group-id": groupId,
159
161
  // e2e test attributes
160
162
  "data-panel-collapsible": undefined,
161
163
  "data-panel-size": undefined
@@ -630,6 +632,7 @@ function getResizeHandlePanelIds(groupId, handleId, panelsArray) {
630
632
 
631
633
  function useWindowSplitterPanelGroupBehavior({
632
634
  committedValuesRef,
635
+ eagerValuesRef,
633
636
  groupId,
634
637
  layout,
635
638
  panelDataArray,
@@ -641,7 +644,7 @@ function useWindowSplitterPanelGroupBehavior({
641
644
  useEffect(() => {
642
645
  const {
643
646
  panelDataArray
644
- } = committedValuesRef.current;
647
+ } = eagerValuesRef.current;
645
648
  const groupElement = getPanelGroupElement(groupId);
646
649
  assert(groupElement != null, `No group found for id "${groupId}"`);
647
650
  const handles = getResizeHandleElementsForGroup(groupId);
@@ -699,7 +702,7 @@ function useWindowSplitterPanelGroupBehavior({
699
702
  return () => {
700
703
  cleanupFunctions.forEach(cleanupFunction => cleanupFunction());
701
704
  };
702
- }, [committedValuesRef, groupId, layout, panelDataArray, setLayout]);
705
+ }, [committedValuesRef, eagerValuesRef, groupId, layout, panelDataArray, setLayout]);
703
706
  }
704
707
 
705
708
  function areEqual(arrayA, arrayB) {
@@ -796,6 +799,44 @@ function calculateDeltaPercentage(event, groupId, dragHandleId, direction, initi
796
799
  }
797
800
  }
798
801
 
802
+ function calculateUnsafeDefaultLayout({
803
+ groupSizePixels,
804
+ panelDataArray
805
+ }) {
806
+ const layout = Array(panelDataArray.length);
807
+ const panelDataConstraints = panelDataArray.map(panelData => panelData.constraints);
808
+ let numPanelsWithSizes = 0;
809
+ let remainingSize = 100;
810
+
811
+ // Distribute default sizes first
812
+ for (let index = 0; index < panelDataArray.length; index++) {
813
+ const {
814
+ defaultSizePercentage
815
+ } = computePercentagePanelConstraints(panelDataConstraints, index, groupSizePixels);
816
+ if (defaultSizePercentage != null) {
817
+ numPanelsWithSizes++;
818
+ layout[index] = defaultSizePercentage;
819
+ remainingSize -= defaultSizePercentage;
820
+ }
821
+ }
822
+
823
+ // Remaining size should be distributed evenly between panels without default sizes
824
+ for (let index = 0; index < panelDataArray.length; index++) {
825
+ const {
826
+ defaultSizePercentage
827
+ } = computePercentagePanelConstraints(panelDataConstraints, index, groupSizePixels);
828
+ if (defaultSizePercentage != null) {
829
+ continue;
830
+ }
831
+ const numRemainingPanels = panelDataArray.length - numPanelsWithSizes;
832
+ const size = remainingSize / numRemainingPanels;
833
+ numPanelsWithSizes++;
834
+ layout[index] = size;
835
+ remainingSize -= size;
836
+ }
837
+ return layout;
838
+ }
839
+
799
840
  function convertPercentageToPixels(percentage, groupSizePixels) {
800
841
  return percentage / 100 * groupSizePixels;
801
842
  }
@@ -946,6 +987,10 @@ function debounce(callback, durationMs = 10) {
946
987
  return callable;
947
988
  }
948
989
 
990
+ function getPanelElementsForGroup(groupId) {
991
+ return Array.from(document.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
992
+ }
993
+
949
994
  // PanelGroup might be rendering in a server-side environment where localStorage is not available
950
995
  // or on a browser with cookies/storage disabled.
951
996
  // In either case, this function avoids accessing localStorage until needed,
@@ -1001,6 +1046,15 @@ function loadSerializedPanelGroupState(autoSaveId, storage) {
1001
1046
  } catch (error) {}
1002
1047
  return null;
1003
1048
  }
1049
+ function loadPanelLayout(autoSaveId, panels, storage) {
1050
+ const state = loadSerializedPanelGroupState(autoSaveId, storage);
1051
+ if (state) {
1052
+ var _state$key;
1053
+ const key = getSerializationKey(panels);
1054
+ return (_state$key = state[key]) !== null && _state$key !== void 0 ? _state$key : null;
1055
+ }
1056
+ return null;
1057
+ }
1004
1058
  function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
1005
1059
  const key = getSerializationKey(panels);
1006
1060
  const state = loadSerializedPanelGroupState(autoSaveId, storage) || {};
@@ -1012,6 +1066,12 @@ function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
1012
1066
  }
1013
1067
  }
1014
1068
 
1069
+ function shouldMonitorPixelBasedConstraints(constraints) {
1070
+ return constraints.some(constraints => {
1071
+ return constraints.collapsedSizePixels !== undefined || constraints.maxSizePixels !== undefined || constraints.minSizePixels !== undefined;
1072
+ });
1073
+ }
1074
+
1015
1075
  // All units must be in percentages; pixel values should be pre-converted
1016
1076
  function validatePanelGroupLayout({
1017
1077
  groupSizePixels,
@@ -1080,7 +1140,7 @@ const defaultStorage = {
1080
1140
  };
1081
1141
  const debounceMap = {};
1082
1142
  function PanelGroupWithForwardedRef({
1083
- autoSaveId,
1143
+ autoSaveId = null,
1084
1144
  children,
1085
1145
  className: classNameFromProps = "",
1086
1146
  dataAttributes,
@@ -1097,20 +1157,22 @@ function PanelGroupWithForwardedRef({
1097
1157
  const groupId = useUniqueId(idFromProps);
1098
1158
  const [dragState, setDragState] = useState(null);
1099
1159
  const [layout, setLayout] = useState([]);
1100
- const [panelDataArray, setPanelDataArray] = useState([]);
1101
1160
  const panelIdToLastNotifiedMixedSizesMapRef = useRef({});
1102
1161
  const panelSizeBeforeCollapseRef = useRef(new Map());
1103
1162
  const prevDeltaRef = useRef(0);
1104
- const [imperativeApiQueue, setImperativeApiQueue] = useState([]);
1105
1163
  const committedValuesRef = useRef({
1164
+ autoSaveId,
1106
1165
  direction,
1107
1166
  dragState,
1108
1167
  id: groupId,
1109
1168
  keyboardResizeByPercentage,
1110
1169
  keyboardResizeByPixels,
1111
- layout,
1112
1170
  onLayout,
1113
- panelDataArray
1171
+ storage
1172
+ });
1173
+ const eagerValuesRef = useRef({
1174
+ layout,
1175
+ panelDataArray: []
1114
1176
  });
1115
1177
  useRef({
1116
1178
  didLogIdAndOrderWarning: false,
@@ -1121,9 +1183,11 @@ function PanelGroupWithForwardedRef({
1121
1183
  getId: () => committedValuesRef.current.id,
1122
1184
  getLayout: () => {
1123
1185
  const {
1124
- id: groupId,
1125
- layout
1186
+ id: groupId
1126
1187
  } = committedValuesRef.current;
1188
+ const {
1189
+ layout
1190
+ } = eagerValuesRef.current;
1127
1191
  const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1128
1192
  return layout.map(sizePercentage => {
1129
1193
  return {
@@ -1135,10 +1199,12 @@ function PanelGroupWithForwardedRef({
1135
1199
  setLayout: mixedSizes => {
1136
1200
  const {
1137
1201
  id: groupId,
1202
+ onLayout
1203
+ } = committedValuesRef.current;
1204
+ const {
1138
1205
  layout: prevLayout,
1139
- onLayout,
1140
1206
  panelDataArray
1141
- } = committedValuesRef.current;
1207
+ } = eagerValuesRef.current;
1142
1208
  const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1143
1209
  const unsafeLayout = mixedSizes.map(mixedSize => getPercentageSizeFromMixedSizes(mixedSize, groupSizePixels));
1144
1210
  const safeLayout = validatePanelGroupLayout({
@@ -1148,6 +1214,7 @@ function PanelGroupWithForwardedRef({
1148
1214
  });
1149
1215
  if (!areEqual(prevLayout, safeLayout)) {
1150
1216
  setLayout(safeLayout);
1217
+ eagerValuesRef.current.layout = safeLayout;
1151
1218
  if (onLayout) {
1152
1219
  onLayout(safeLayout.map(sizePercentage => ({
1153
1220
  sizePercentage,
@@ -1158,14 +1225,20 @@ function PanelGroupWithForwardedRef({
1158
1225
  }
1159
1226
  }
1160
1227
  }), []);
1228
+
1161
1229
  useWindowSplitterPanelGroupBehavior({
1162
1230
  committedValuesRef,
1231
+ eagerValuesRef,
1163
1232
  groupId,
1164
1233
  layout,
1165
- panelDataArray,
1234
+ panelDataArray: eagerValuesRef.current.panelDataArray,
1166
1235
  setLayout
1167
1236
  });
1168
1237
  useEffect(() => {
1238
+ const {
1239
+ panelDataArray
1240
+ } = eagerValuesRef.current;
1241
+
1169
1242
  // If this panel has been configured to persist sizing information, save sizes to local storage.
1170
1243
  if (autoSaveId) {
1171
1244
  if (layout.length === 0 || layout.length !== panelDataArray.length) {
@@ -1178,7 +1251,7 @@ function PanelGroupWithForwardedRef({
1178
1251
  }
1179
1252
  debounceMap[autoSaveId](autoSaveId, panelDataArray, layout, storage);
1180
1253
  }
1181
- }, [autoSaveId, layout, panelDataArray, storage]);
1254
+ }, [autoSaveId, layout, storage]);
1182
1255
 
1183
1256
  // DEV warnings
1184
1257
  useEffect(() => {
@@ -1186,22 +1259,13 @@ function PanelGroupWithForwardedRef({
1186
1259
 
1187
1260
  // External APIs are safe to memoize via committed values ref
1188
1261
  const collapsePanel = useCallback(panelData => {
1262
+ const {
1263
+ onLayout
1264
+ } = committedValuesRef.current;
1189
1265
  const {
1190
1266
  layout: prevLayout,
1191
- onLayout,
1192
1267
  panelDataArray
1193
- } = committedValuesRef.current;
1194
-
1195
- // See issues/211
1196
- if (panelDataArray.find(({
1197
- id
1198
- }) => id === panelData.id) == null) {
1199
- setImperativeApiQueue(prev => [...prev, {
1200
- panelData,
1201
- type: "collapse"
1202
- }]);
1203
- return;
1204
- }
1268
+ } = eagerValuesRef.current;
1205
1269
  if (panelData.constraints.collapsible) {
1206
1270
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1207
1271
  const {
@@ -1226,6 +1290,7 @@ function PanelGroupWithForwardedRef({
1226
1290
  });
1227
1291
  if (!compareLayouts(prevLayout, nextLayout)) {
1228
1292
  setLayout(nextLayout);
1293
+ eagerValuesRef.current.layout = nextLayout;
1229
1294
  if (onLayout) {
1230
1295
  onLayout(nextLayout.map(sizePercentage => ({
1231
1296
  sizePercentage,
@@ -1240,22 +1305,13 @@ function PanelGroupWithForwardedRef({
1240
1305
 
1241
1306
  // External APIs are safe to memoize via committed values ref
1242
1307
  const expandPanel = useCallback(panelData => {
1308
+ const {
1309
+ onLayout
1310
+ } = committedValuesRef.current;
1243
1311
  const {
1244
1312
  layout: prevLayout,
1245
- onLayout,
1246
1313
  panelDataArray
1247
- } = committedValuesRef.current;
1248
-
1249
- // See issues/211
1250
- if (panelDataArray.find(({
1251
- id
1252
- }) => id === panelData.id) == null) {
1253
- setImperativeApiQueue(prev => [...prev, {
1254
- panelData,
1255
- type: "expand"
1256
- }]);
1257
- return;
1258
- }
1314
+ } = eagerValuesRef.current;
1259
1315
  if (panelData.constraints.collapsible) {
1260
1316
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1261
1317
  const {
@@ -1281,6 +1337,7 @@ function PanelGroupWithForwardedRef({
1281
1337
  });
1282
1338
  if (!compareLayouts(prevLayout, nextLayout)) {
1283
1339
  setLayout(nextLayout);
1340
+ eagerValuesRef.current.layout = nextLayout;
1284
1341
  if (onLayout) {
1285
1342
  onLayout(nextLayout.map(sizePercentage => ({
1286
1343
  sizePercentage,
@@ -1298,7 +1355,7 @@ function PanelGroupWithForwardedRef({
1298
1355
  const {
1299
1356
  layout,
1300
1357
  panelDataArray
1301
- } = committedValuesRef.current;
1358
+ } = eagerValuesRef.current;
1302
1359
  const {
1303
1360
  panelSizePercentage,
1304
1361
  panelSizePixels
@@ -1311,6 +1368,9 @@ function PanelGroupWithForwardedRef({
1311
1368
 
1312
1369
  // This API should never read from committedValuesRef
1313
1370
  const getPanelStyle = useCallback(panelData => {
1371
+ const {
1372
+ panelDataArray
1373
+ } = eagerValuesRef.current;
1314
1374
  const panelIndex = panelDataArray.indexOf(panelData);
1315
1375
  return computePanelFlexBoxStyle({
1316
1376
  dragState,
@@ -1318,14 +1378,14 @@ function PanelGroupWithForwardedRef({
1318
1378
  panelData: panelDataArray,
1319
1379
  panelIndex
1320
1380
  });
1321
- }, [dragState, layout, panelDataArray]);
1381
+ }, [dragState, layout]);
1322
1382
 
1323
1383
  // External APIs are safe to memoize via committed values ref
1324
1384
  const isPanelCollapsed = useCallback(panelData => {
1325
1385
  const {
1326
1386
  layout,
1327
1387
  panelDataArray
1328
- } = committedValuesRef.current;
1388
+ } = eagerValuesRef.current;
1329
1389
  const {
1330
1390
  collapsedSizePercentage,
1331
1391
  collapsible,
@@ -1339,7 +1399,7 @@ function PanelGroupWithForwardedRef({
1339
1399
  const {
1340
1400
  layout,
1341
1401
  panelDataArray
1342
- } = committedValuesRef.current;
1402
+ } = eagerValuesRef.current;
1343
1403
  const {
1344
1404
  collapsedSizePercentage,
1345
1405
  collapsible,
@@ -1348,22 +1408,82 @@ function PanelGroupWithForwardedRef({
1348
1408
  return !collapsible || panelSizePercentage > collapsedSizePercentage;
1349
1409
  }, [groupId]);
1350
1410
  const registerPanel = useCallback(panelData => {
1351
- setPanelDataArray(prevPanelDataArray => {
1352
- const nextPanelDataArray = [...prevPanelDataArray, panelData];
1353
- return nextPanelDataArray.sort((panelA, panelB) => {
1354
- const orderA = panelA.order;
1355
- const orderB = panelB.order;
1356
- if (orderA == null && orderB == null) {
1357
- return 0;
1358
- } else if (orderA == null) {
1359
- return -1;
1360
- } else if (orderB == null) {
1361
- return 1;
1362
- } else {
1363
- return orderA - orderB;
1364
- }
1411
+ const {
1412
+ autoSaveId,
1413
+ id: groupId,
1414
+ onLayout,
1415
+ storage
1416
+ } = committedValuesRef.current;
1417
+ const {
1418
+ layout: prevLayout,
1419
+ panelDataArray
1420
+ } = eagerValuesRef.current;
1421
+ panelDataArray.push(panelData);
1422
+ panelDataArray.sort((panelA, panelB) => {
1423
+ const orderA = panelA.order;
1424
+ const orderB = panelB.order;
1425
+ if (orderA == null && orderB == null) {
1426
+ return 0;
1427
+ } else if (orderA == null) {
1428
+ return -1;
1429
+ } else if (orderB == null) {
1430
+ return 1;
1431
+ } else {
1432
+ return orderA - orderB;
1433
+ }
1434
+ });
1435
+
1436
+ // Wait until all panels have registered before we try to compute layout;
1437
+ // doing it earlier is both wasteful and may trigger misleading warnings in development mode.
1438
+ const panelElements = getPanelElementsForGroup(groupId);
1439
+ if (panelElements.length !== panelDataArray.length) {
1440
+ return;
1441
+ }
1442
+
1443
+ // If this panel has been configured to persist sizing information,
1444
+ // default size should be restored from local storage if possible.
1445
+ let unsafeLayout = null;
1446
+ if (autoSaveId) {
1447
+ unsafeLayout = loadPanelLayout(autoSaveId, panelDataArray, storage);
1448
+ }
1449
+ const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1450
+ if (groupSizePixels <= 0) {
1451
+ if (shouldMonitorPixelBasedConstraints(panelDataArray.map(({
1452
+ constraints
1453
+ }) => constraints))) {
1454
+ // Wait until the group has rendered a non-zero size before computing layout.
1455
+ return;
1456
+ }
1457
+ }
1458
+ if (unsafeLayout == null) {
1459
+ unsafeLayout = calculateUnsafeDefaultLayout({
1460
+ groupSizePixels,
1461
+ panelDataArray
1365
1462
  });
1463
+ }
1464
+
1465
+ // Validate even saved layouts in case something has changed since last render
1466
+ // e.g. for pixel groups, this could be the size of the window
1467
+ const nextLayout = validatePanelGroupLayout({
1468
+ groupSizePixels,
1469
+ layout: unsafeLayout,
1470
+ panelConstraints: panelDataArray.map(panelData => panelData.constraints)
1366
1471
  });
1472
+
1473
+ // Offscreen mode makes this a bit weird;
1474
+ // Panels unregister when hidden and re-register when shown again,
1475
+ // but the overall layout doesn't change between these two cases.
1476
+ setLayout(nextLayout);
1477
+ eagerValuesRef.current.layout = nextLayout;
1478
+ if (!areEqual(prevLayout, nextLayout)) {
1479
+ if (onLayout) {
1480
+ onLayout(nextLayout.map(sizePercentage => ({
1481
+ sizePercentage,
1482
+ sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
1483
+ })));
1484
+ }
1485
+ callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
1486
+ }
1367
1487
  }, []);
1368
1488
  const registerResizeHandle = useCallback(dragHandleId => {
1369
1489
  return function resizeHandler(event) {
@@ -1374,10 +1494,12 @@ function PanelGroupWithForwardedRef({
1374
1494
  id: groupId,
1375
1495
  keyboardResizeByPercentage,
1376
1496
  keyboardResizeByPixels,
1377
- onLayout,
1378
- panelDataArray,
1379
- layout: prevLayout
1497
+ onLayout
1380
1498
  } = committedValuesRef.current;
1499
+ const {
1500
+ layout: prevLayout,
1501
+ panelDataArray
1502
+ } = eagerValuesRef.current;
1381
1503
  const {
1382
1504
  initialLayout
1383
1505
  } = dragState !== null && dragState !== void 0 ? dragState : {};
@@ -1433,6 +1555,7 @@ function PanelGroupWithForwardedRef({
1433
1555
  }
1434
1556
  if (layoutChanged) {
1435
1557
  setLayout(nextLayout);
1558
+ eagerValuesRef.current.layout = nextLayout;
1436
1559
  if (onLayout) {
1437
1560
  onLayout(nextLayout.map(sizePercentage => ({
1438
1561
  sizePercentage,
@@ -1446,23 +1569,13 @@ function PanelGroupWithForwardedRef({
1446
1569
 
1447
1570
  // External APIs are safe to memoize via committed values ref
1448
1571
  const resizePanel = useCallback((panelData, mixedSizes) => {
1572
+ const {
1573
+ onLayout
1574
+ } = committedValuesRef.current;
1449
1575
  const {
1450
1576
  layout: prevLayout,
1451
- onLayout,
1452
1577
  panelDataArray
1453
- } = committedValuesRef.current;
1454
-
1455
- // See issues/211
1456
- if (panelDataArray.find(({
1457
- id
1458
- }) => id === panelData.id) == null) {
1459
- setImperativeApiQueue(prev => [...prev, {
1460
- panelData,
1461
- mixedSizes,
1462
- type: "resize"
1463
- }]);
1464
- return;
1465
- }
1578
+ } = eagerValuesRef.current;
1466
1579
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1467
1580
  const {
1468
1581
  groupSizePixels,
@@ -1482,6 +1595,7 @@ function PanelGroupWithForwardedRef({
1482
1595
  });
1483
1596
  if (!compareLayouts(prevLayout, nextLayout)) {
1484
1597
  setLayout(nextLayout);
1598
+ eagerValuesRef.current.layout = nextLayout;
1485
1599
  if (onLayout) {
1486
1600
  onLayout(nextLayout.map(sizePercentage => ({
1487
1601
  sizePercentage,
@@ -1493,9 +1607,11 @@ function PanelGroupWithForwardedRef({
1493
1607
  }, [groupId]);
1494
1608
  const startDragging = useCallback((dragHandleId, event) => {
1495
1609
  const {
1496
- direction,
1497
- layout
1610
+ direction
1498
1611
  } = committedValuesRef.current;
1612
+ const {
1613
+ layout
1614
+ } = eagerValuesRef.current;
1499
1615
  const handleElement = getResizeHandleElement(dragHandleId);
1500
1616
  const initialCursorPosition = getResizeEventCursorPosition(direction, event);
1501
1617
  setDragState({
@@ -1509,16 +1625,86 @@ function PanelGroupWithForwardedRef({
1509
1625
  resetGlobalCursorStyle();
1510
1626
  setDragState(null);
1511
1627
  }, []);
1628
+ const unregisterPanelRef = useRef({
1629
+ pendingPanelIds: new Set(),
1630
+ timeout: null
1631
+ });
1512
1632
  const unregisterPanel = useCallback(panelData => {
1513
- delete panelIdToLastNotifiedMixedSizesMapRef.current[panelData.id];
1514
- setPanelDataArray(panelDataArray => {
1515
- const index = panelDataArray.indexOf(panelData);
1516
- if (index >= 0) {
1517
- panelDataArray = [...panelDataArray];
1518
- panelDataArray.splice(index, 1);
1633
+ const {
1634
+ id: groupId,
1635
+ onLayout
1636
+ } = committedValuesRef.current;
1637
+ const {
1638
+ layout: prevLayout,
1639
+ panelDataArray
1640
+ } = eagerValuesRef.current;
1641
+ const index = panelDataArray.indexOf(panelData);
1642
+ if (index >= 0) {
1643
+ panelDataArray.splice(index, 1);
1644
+ unregisterPanelRef.current.pendingPanelIds.add(panelData.id);
1645
+ }
1646
+ if (unregisterPanelRef.current.timeout != null) {
1647
+ clearTimeout(unregisterPanelRef.current.timeout);
1648
+ }
1649
+
1650
+ // Batch panel unmounts so that we only calculate layout once;
1651
+ // This is more efficient and avoids misleading warnings in development mode.
1652
+ // We can't check the DOM to detect this because Panel elements have not yet been removed.
1653
+ unregisterPanelRef.current.timeout = setTimeout(() => {
1654
+ const {
1655
+ pendingPanelIds
1656
+ } = unregisterPanelRef.current;
1657
+ const map = panelIdToLastNotifiedMixedSizesMapRef.current;
1658
+
1659
+ // TRICKY
1660
+ // Strict effects mode
1661
+ let unmountDueToStrictMode = false;
1662
+ pendingPanelIds.forEach(panelId => {
1663
+ pendingPanelIds.delete(panelId);
1664
+ if (panelDataArray.find(({
1665
+ id
1666
+ }) => id === panelId) == null) {
1667
+ unmountDueToStrictMode = true;
1668
+
1669
+ // TRICKY
1670
+ // When a panel is removed from the group, we should delete the most recent prev-size entry for it.
1671
+ // If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
1672
+ // Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
1673
+ delete map[panelData.id];
1674
+ }
1675
+ });
1676
+ if (!unmountDueToStrictMode) {
1677
+ return;
1519
1678
  }
1520
- return panelDataArray;
1521
- });
1679
+ if (panelDataArray.length === 0) {
1680
+ // The group is unmounting; skip layout calculation.
1681
+ return;
1682
+ }
1683
+ const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1684
+ let unsafeLayout = calculateUnsafeDefaultLayout({
1685
+ groupSizePixels,
1686
+ panelDataArray
1687
+ });
1688
+
1689
+ // Validate even saved layouts in case something has changed since last render
1690
+ // e.g. for pixel groups, this could be the size of the window
1691
+ const nextLayout = validatePanelGroupLayout({
1692
+ groupSizePixels,
1693
+ layout: unsafeLayout,
1694
+ panelConstraints: panelDataArray.map(panelData => panelData.constraints)
1695
+ });
1696
+ if (!areEqual(prevLayout, nextLayout)) {
1697
+ setLayout(nextLayout);
1698
+ eagerValuesRef.current.layout = nextLayout;
1699
+ if (onLayout) {
1700
+ onLayout(nextLayout.map(sizePercentage => ({
1701
+ sizePercentage,
1702
+ sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
1703
+ })));
1704
+ }
1705
+ callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
1706
+ }
1707
+ }, 0);
1522
1708
  }, []);
1523
1709
  const context = useMemo(() => ({
1524
1710
  collapsePanel,