react-resizable-panels 1.0.7 → 1.0.9

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +16 -15
  3. package/dist/declarations/src/Panel.d.ts +3 -3
  4. package/dist/declarations/src/PanelGroup.d.ts +2 -2
  5. package/dist/declarations/src/PanelResizeHandle.d.ts +2 -2
  6. package/dist/declarations/src/index.d.ts +1 -3
  7. package/dist/declarations/src/utils/dom/getPanelElement.d.ts +1 -1
  8. package/dist/declarations/src/utils/dom/getPanelElementsForGroup.d.ts +1 -1
  9. package/dist/declarations/src/utils/dom/getPanelGroupElement.d.ts +1 -1
  10. package/dist/declarations/src/utils/dom/getResizeHandleElement.d.ts +1 -1
  11. package/dist/declarations/src/utils/dom/getResizeHandleElementIndex.d.ts +1 -1
  12. package/dist/declarations/src/utils/dom/getResizeHandleElementsForGroup.d.ts +1 -1
  13. package/dist/declarations/src/utils/dom/getResizeHandlePanelIds.d.ts +1 -1
  14. package/dist/declarations/src/vendor/react.d.ts +2 -2
  15. package/dist/react-resizable-panels.browser.cjs.js +114 -84
  16. package/dist/react-resizable-panels.browser.cjs.mjs +0 -2
  17. package/dist/react-resizable-panels.browser.development.cjs.js +116 -85
  18. package/dist/react-resizable-panels.browser.development.cjs.mjs +0 -2
  19. package/dist/react-resizable-panels.browser.development.esm.js +117 -84
  20. package/dist/react-resizable-panels.browser.esm.js +115 -83
  21. package/dist/react-resizable-panels.cjs.js +114 -84
  22. package/dist/react-resizable-panels.cjs.mjs +0 -2
  23. package/dist/react-resizable-panels.development.cjs.js +116 -85
  24. package/dist/react-resizable-panels.development.cjs.mjs +0 -2
  25. package/dist/react-resizable-panels.development.esm.js +117 -84
  26. package/dist/react-resizable-panels.development.node.cjs.js +102 -83
  27. package/dist/react-resizable-panels.development.node.cjs.mjs +0 -2
  28. package/dist/react-resizable-panels.development.node.esm.js +103 -82
  29. package/dist/react-resizable-panels.esm.js +115 -83
  30. package/dist/react-resizable-panels.node.cjs.js +100 -82
  31. package/dist/react-resizable-panels.node.cjs.mjs +0 -2
  32. package/dist/react-resizable-panels.node.esm.js +101 -81
  33. package/package.json +1 -1
  34. package/src/Panel.test.tsx +137 -2
  35. package/src/Panel.ts +16 -2
  36. package/src/PanelGroup.test.tsx +3 -2
  37. package/src/PanelGroup.ts +95 -35
  38. package/src/PanelGroupContext.ts +9 -3
  39. package/src/PanelResizeHandle.test.tsx +3 -3
  40. package/src/PanelResizeHandle.ts +4 -2
  41. package/src/hooks/useWindowSplitterBehavior.ts +14 -5
  42. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +23 -7
  43. package/src/index.ts +0 -4
  44. package/src/utils/calculateDeltaPercentage.ts +4 -2
  45. package/src/utils/calculateDragOffsetPercentage.ts +4 -3
  46. package/src/utils/determinePivotIndices.ts +7 -2
  47. package/src/utils/dom/getPanelElement.ts +5 -2
  48. package/src/utils/dom/getPanelElementsForGroup.ts +5 -2
  49. package/src/utils/dom/getPanelGroupElement.ts +14 -2
  50. package/src/utils/dom/getResizeHandleElement.ts +5 -4
  51. package/src/utils/dom/getResizeHandleElementIndex.ts +3 -2
  52. package/src/utils/dom/getResizeHandleElementsForGroup.ts +3 -2
  53. package/src/utils/dom/getResizeHandlePanelIds.ts +4 -3
  54. package/src/utils/validatePanelConstraints.test.ts +45 -0
  55. package/src/utils/validatePanelConstraints.ts +5 -1
  56. package/src/vendor/react.ts +2 -0
  57. package/dist/declarations/src/utils/dom/calculateAvailablePanelSizeInPixels.d.ts +0 -1
  58. package/dist/declarations/src/utils/dom/getAvailableGroupSizePixels.d.ts +0 -1
  59. package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +0 -29
  60. package/src/utils/dom/getAvailableGroupSizePixels.ts +0 -29
@@ -65,6 +65,7 @@ function PanelWithForwardedRef({
65
65
  getPanelStyle,
66
66
  groupId,
67
67
  isPanelCollapsed,
68
+ reevaluatePanelConstraints,
68
69
  registerPanel,
69
70
  resizePanel,
70
71
  unregisterPanel
@@ -430,41 +431,48 @@ function adjustLayoutByDelta({
430
431
  return nextLayout;
431
432
  }
432
433
 
433
- function getResizeHandleElementsForGroup(groupId) {
434
- return Array.from(document.querySelectorAll(`[data-panel-resize-handle-id][data-panel-group-id="${groupId}"]`));
434
+ function getResizeHandleElementsForGroup(groupId, scope = document) {
435
+ return Array.from(scope.querySelectorAll(`[data-panel-resize-handle-id][data-panel-group-id="${groupId}"]`));
435
436
  }
436
437
 
437
- function getResizeHandleElementIndex(groupId, id) {
438
- const handles = getResizeHandleElementsForGroup(groupId);
438
+ function getResizeHandleElementIndex(groupId, id, scope = document) {
439
+ const handles = getResizeHandleElementsForGroup(groupId, scope);
439
440
  const index = handles.findIndex(handle => handle.getAttribute("data-panel-resize-handle-id") === id);
440
441
  return index !== null && index !== void 0 ? index : null;
441
442
  }
442
443
 
443
- function determinePivotIndices(groupId, dragHandleId) {
444
- const index = getResizeHandleElementIndex(groupId, dragHandleId);
444
+ function determinePivotIndices(groupId, dragHandleId, panelGroupElement) {
445
+ const index = getResizeHandleElementIndex(groupId, dragHandleId, panelGroupElement);
445
446
  return index != null ? [index, index + 1] : [-1, -1];
446
447
  }
447
448
 
448
- function getPanelGroupElement(id) {
449
- const element = document.querySelector(`[data-panel-group][data-panel-group-id="${id}"]`);
449
+ function getPanelGroupElement(id, rootElement = document) {
450
+ var _dataset;
451
+ //If the root element is the PanelGroup
452
+ if (rootElement instanceof HTMLElement && (rootElement === null || rootElement === void 0 ? void 0 : (_dataset = rootElement.dataset) === null || _dataset === void 0 ? void 0 : _dataset.panelGroupId) == id) {
453
+ return rootElement;
454
+ }
455
+
456
+ //Else query children
457
+ const element = rootElement.querySelector(`[data-panel-group][data-panel-group-id="${id}"]`);
450
458
  if (element) {
451
459
  return element;
452
460
  }
453
461
  return null;
454
462
  }
455
463
 
456
- function getResizeHandleElement(id) {
457
- const element = document.querySelector(`[data-panel-resize-handle-id="${id}"]`);
464
+ function getResizeHandleElement(id, scope = document) {
465
+ const element = scope.querySelector(`[data-panel-resize-handle-id="${id}"]`);
458
466
  if (element) {
459
467
  return element;
460
468
  }
461
469
  return null;
462
470
  }
463
471
 
464
- function getResizeHandlePanelIds(groupId, handleId, panelsArray) {
472
+ function getResizeHandlePanelIds(groupId, handleId, panelsArray, scope = document) {
465
473
  var _panelsArray$index$id, _panelsArray$index, _panelsArray$id, _panelsArray;
466
- const handle = getResizeHandleElement(handleId);
467
- const handles = getResizeHandleElementsForGroup(groupId);
474
+ const handle = getResizeHandleElement(handleId, scope);
475
+ const handles = getResizeHandleElementsForGroup(groupId, scope);
468
476
  const index = handle ? handles.indexOf(handle) : -1;
469
477
  const idBefore = (_panelsArray$index$id = (_panelsArray$index = panelsArray[index]) === null || _panelsArray$index === void 0 ? void 0 : _panelsArray$index.id) !== null && _panelsArray$index$id !== void 0 ? _panelsArray$index$id : null;
470
478
  const idAfter = (_panelsArray$id = (_panelsArray = panelsArray[index + 1]) === null || _panelsArray === void 0 ? void 0 : _panelsArray.id) !== null && _panelsArray$id !== void 0 ? _panelsArray$id : null;
@@ -479,25 +487,29 @@ function useWindowSplitterPanelGroupBehavior({
479
487
  groupId,
480
488
  layout,
481
489
  panelDataArray,
490
+ panelGroupElement,
482
491
  setLayout
483
492
  }) {
484
493
  useRef({
485
494
  didWarnAboutMissingResizeHandle: false
486
495
  });
487
496
  useEffect(() => {
497
+ if (!panelGroupElement) {
498
+ return;
499
+ }
488
500
  const eagerValues = eagerValuesRef.current;
489
501
  assert(eagerValues);
490
502
  const {
491
503
  panelDataArray
492
504
  } = eagerValues;
493
- const groupElement = getPanelGroupElement(groupId);
505
+ const groupElement = getPanelGroupElement(groupId, panelGroupElement);
494
506
  assert(groupElement != null, `No group found for id "${groupId}"`);
495
- const handles = getResizeHandleElementsForGroup(groupId);
507
+ const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
496
508
  assert(handles);
497
509
  const cleanupFunctions = handles.map(handle => {
498
510
  const handleId = handle.getAttribute("data-panel-resize-handle-id");
499
511
  assert(handleId);
500
- const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelDataArray);
512
+ const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelDataArray, panelGroupElement);
501
513
  if (idBefore == null || idAfter == null) {
502
514
  return () => {};
503
515
  }
@@ -524,7 +536,7 @@ function useWindowSplitterPanelGroupBehavior({
524
536
  delta: fuzzyNumbersEqual(size, collapsedSize) ? minSize - collapsedSize : collapsedSize - size,
525
537
  layout,
526
538
  panelConstraints: panelDataArray.map(panelData => panelData.constraints),
527
- pivotIndices: determinePivotIndices(groupId, handleId),
539
+ pivotIndices: determinePivotIndices(groupId, handleId, panelGroupElement),
528
540
  trigger: "keyboard"
529
541
  });
530
542
  if (layout !== nextLayout) {
@@ -544,7 +556,7 @@ function useWindowSplitterPanelGroupBehavior({
544
556
  return () => {
545
557
  cleanupFunctions.forEach(cleanupFunction => cleanupFunction());
546
558
  };
547
- }, [committedValuesRef, eagerValuesRef, groupId, layout, panelDataArray, setLayout]);
559
+ }, [panelGroupElement, committedValuesRef, eagerValuesRef, groupId, layout, panelDataArray, setLayout]);
548
560
  }
549
561
 
550
562
  function areEqual(arrayA, arrayB) {
@@ -582,9 +594,9 @@ function getResizeEventCursorPosition(direction, event) {
582
594
  }
583
595
  }
584
596
 
585
- function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState) {
597
+ function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState, panelGroupElement) {
586
598
  const isHorizontal = direction === "horizontal";
587
- const handleElement = getResizeHandleElement(dragHandleId);
599
+ const handleElement = getResizeHandleElement(dragHandleId, panelGroupElement);
588
600
  assert(handleElement);
589
601
  const groupId = handleElement.getAttribute("data-panel-group-id");
590
602
  assert(groupId);
@@ -592,7 +604,7 @@ function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDr
592
604
  initialCursorPosition
593
605
  } = initialDragState;
594
606
  const cursorPosition = getResizeEventCursorPosition(direction, event);
595
- const groupElement = getPanelGroupElement(groupId);
607
+ const groupElement = getPanelGroupElement(groupId, panelGroupElement);
596
608
  assert(groupElement);
597
609
  const groupRect = groupElement.getBoundingClientRect();
598
610
  const groupSizeInPixels = isHorizontal ? groupRect.width : groupRect.height;
@@ -602,7 +614,7 @@ function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDr
602
614
  }
603
615
 
604
616
  // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
605
- function calculateDeltaPercentage(event, dragHandleId, direction, initialDragState, keyboardResizeBy) {
617
+ function calculateDeltaPercentage(event, dragHandleId, direction, initialDragState, keyboardResizeBy, panelGroupElement) {
606
618
  if (isKeyDown(event)) {
607
619
  const isHorizontal = direction === "horizontal";
608
620
  let delta = 0;
@@ -639,7 +651,7 @@ function calculateDeltaPercentage(event, dragHandleId, direction, initialDragSta
639
651
  if (initialDragState == null) {
640
652
  return 0;
641
653
  }
642
- return calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState);
654
+ return calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState, panelGroupElement);
643
655
  }
644
656
  }
645
657
 
@@ -944,6 +956,7 @@ function PanelGroupWithForwardedRef({
944
956
  ...rest
945
957
  }) {
946
958
  const groupId = useUniqueId(idFromProps);
959
+ const panelGroupElementRef = useRef(null);
947
960
  const [dragState, setDragState] = useState(null);
948
961
  const [layout, setLayout] = useState([]);
949
962
  const panelIdToLastNotifiedSizeMapRef = useRef({});
@@ -1004,7 +1017,8 @@ function PanelGroupWithForwardedRef({
1004
1017
  groupId,
1005
1018
  layout,
1006
1019
  panelDataArray: eagerValuesRef.current.panelDataArray,
1007
- setLayout
1020
+ setLayout,
1021
+ panelGroupElement: panelGroupElementRef.current
1008
1022
  });
1009
1023
  useEffect(() => {
1010
1024
  const {
@@ -1199,6 +1213,10 @@ function PanelGroupWithForwardedRef({
1199
1213
  const registerResizeHandle = useCallback(dragHandleId => {
1200
1214
  return function resizeHandler(event) {
1201
1215
  event.preventDefault();
1216
+ const panelGroupElement = panelGroupElementRef.current;
1217
+ if (!panelGroupElement) {
1218
+ return () => null;
1219
+ }
1202
1220
  const {
1203
1221
  direction,
1204
1222
  dragState,
@@ -1213,8 +1231,8 @@ function PanelGroupWithForwardedRef({
1213
1231
  const {
1214
1232
  initialLayout
1215
1233
  } = dragState !== null && dragState !== void 0 ? dragState : {};
1216
- const pivotIndices = determinePivotIndices(groupId, dragHandleId);
1217
- let delta = calculateDeltaPercentage(event, dragHandleId, direction, dragState, keyboardResizeBy);
1234
+ const pivotIndices = determinePivotIndices(groupId, dragHandleId, panelGroupElement);
1235
+ let delta = calculateDeltaPercentage(event, dragHandleId, direction, dragState, keyboardResizeBy, panelGroupElement);
1218
1236
  if (delta === 0) {
1219
1237
  return;
1220
1238
  }
@@ -1302,6 +1320,37 @@ function PanelGroupWithForwardedRef({
1302
1320
  callPanelCallbacks(panelDataArray, nextLayout, panelIdToLastNotifiedSizeMapRef.current);
1303
1321
  }
1304
1322
  }, []);
1323
+ const reevaluatePanelConstraints = useCallback((panelData, prevConstraints) => {
1324
+ const {
1325
+ layout,
1326
+ panelDataArray
1327
+ } = eagerValuesRef.current;
1328
+ const {
1329
+ collapsedSize: prevCollapsedSize = 0,
1330
+ collapsible: prevCollapsible,
1331
+ defaultSize: prevDefaultSize,
1332
+ maxSize: prevMaxSize = 100,
1333
+ minSize: prevMinSize = 0
1334
+ } = prevConstraints;
1335
+ const {
1336
+ collapsedSize: nextCollapsedSize = 0,
1337
+ collapsible: nextCollapsible,
1338
+ defaultSize: nextDefaultSize,
1339
+ maxSize: nextMaxSize = 100,
1340
+ minSize: nextMinSize = 0
1341
+ } = panelData.constraints;
1342
+ const {
1343
+ panelSize: prevPanelSize
1344
+ } = panelDataHelper(panelDataArray, panelData, layout);
1345
+ assert(prevPanelSize != null);
1346
+ if (prevCollapsible && nextCollapsible && prevCollapsedSize !== nextCollapsedSize && prevPanelSize === prevCollapsedSize) {
1347
+ resizePanel(panelData, nextCollapsedSize);
1348
+ } else if (prevPanelSize < nextMinSize) {
1349
+ resizePanel(panelData, nextMinSize);
1350
+ } else if (prevPanelSize > nextMaxSize) {
1351
+ resizePanel(panelData, nextMaxSize);
1352
+ }
1353
+ }, [resizePanel]);
1305
1354
  const startDragging = useCallback((dragHandleId, event) => {
1306
1355
  const {
1307
1356
  direction
@@ -1309,7 +1358,10 @@ function PanelGroupWithForwardedRef({
1309
1358
  const {
1310
1359
  layout
1311
1360
  } = eagerValuesRef.current;
1312
- const handleElement = getResizeHandleElement(dragHandleId);
1361
+ if (!panelGroupElementRef.current) {
1362
+ return;
1363
+ }
1364
+ const handleElement = getResizeHandleElement(dragHandleId, panelGroupElementRef.current);
1313
1365
  assert(handleElement);
1314
1366
  const initialCursorPosition = getResizeEventCursorPosition(direction, event);
1315
1367
  setDragState({
@@ -1349,13 +1401,15 @@ function PanelGroupWithForwardedRef({
1349
1401
  groupId,
1350
1402
  isPanelCollapsed,
1351
1403
  isPanelExpanded,
1404
+ reevaluatePanelConstraints,
1352
1405
  registerPanel,
1353
1406
  registerResizeHandle,
1354
1407
  resizePanel,
1355
1408
  startDragging,
1356
1409
  stopDragging,
1357
- unregisterPanel
1358
- }), [collapsePanel, dragState, direction, expandPanel, getPanelSize, getPanelStyle, groupId, isPanelCollapsed, isPanelExpanded, registerPanel, registerResizeHandle, resizePanel, startDragging, stopDragging, unregisterPanel]);
1410
+ unregisterPanel,
1411
+ panelGroupElement: panelGroupElementRef.current
1412
+ }), [collapsePanel, dragState, direction, expandPanel, getPanelSize, getPanelStyle, groupId, isPanelCollapsed, isPanelExpanded, reevaluatePanelConstraints, registerPanel, registerResizeHandle, resizePanel, startDragging, stopDragging, unregisterPanel]);
1359
1413
  const style = {
1360
1414
  display: "flex",
1361
1415
  flexDirection: direction === "horizontal" ? "row" : "column",
@@ -1373,6 +1427,7 @@ function PanelGroupWithForwardedRef({
1373
1427
  ...style,
1374
1428
  ...styleFromProps
1375
1429
  },
1430
+ ref: panelGroupElementRef,
1376
1431
  // CSS selectors
1377
1432
  "data-panel-group": "",
1378
1433
  "data-panel-group-direction": direction,
@@ -1389,14 +1444,12 @@ function findPanelDataIndex(panelDataArray, panelData) {
1389
1444
  return panelDataArray.findIndex(prevPanelData => prevPanelData === panelData || prevPanelData.id === panelData.id);
1390
1445
  }
1391
1446
  function panelDataHelper(panelDataArray, panelData, layout) {
1392
- const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1393
1447
  const panelIndex = findPanelDataIndex(panelDataArray, panelData);
1394
- const panelConstraints = panelConstraintsArray[panelIndex];
1395
1448
  const isLastPanel = panelIndex === panelDataArray.length - 1;
1396
1449
  const pivotIndices = isLastPanel ? [panelIndex - 1, panelIndex] : [panelIndex, panelIndex + 1];
1397
1450
  const panelSize = layout[panelIndex];
1398
1451
  return {
1399
- ...panelConstraints,
1452
+ ...panelData.constraints,
1400
1453
  panelSize,
1401
1454
  pivotIndices
1402
1455
  };
@@ -1407,13 +1460,14 @@ function panelDataHelper(panelDataArray, panelData, layout) {
1407
1460
  function useWindowSplitterResizeHandlerBehavior({
1408
1461
  disabled,
1409
1462
  handleId,
1410
- resizeHandler
1463
+ resizeHandler,
1464
+ panelGroupElement
1411
1465
  }) {
1412
1466
  useEffect(() => {
1413
- if (disabled || resizeHandler == null) {
1467
+ if (disabled || resizeHandler == null || panelGroupElement == null) {
1414
1468
  return;
1415
1469
  }
1416
- const handleElement = getResizeHandleElement(handleId);
1470
+ const handleElement = getResizeHandleElement(handleId, panelGroupElement);
1417
1471
  if (handleElement == null) {
1418
1472
  return;
1419
1473
  }
@@ -1438,8 +1492,8 @@ function useWindowSplitterResizeHandlerBehavior({
1438
1492
  event.preventDefault();
1439
1493
  const groupId = handleElement.getAttribute("data-panel-group-id");
1440
1494
  assert(groupId);
1441
- const handles = getResizeHandleElementsForGroup(groupId);
1442
- const index = getResizeHandleElementIndex(groupId, handleId);
1495
+ const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
1496
+ const index = getResizeHandleElementIndex(groupId, handleId, panelGroupElement);
1443
1497
  assert(index !== null);
1444
1498
  const nextIndex = event.shiftKey ? index > 0 ? index - 1 : handles.length - 1 : index + 1 < handles.length ? index + 1 : 0;
1445
1499
  const nextHandle = handles[nextIndex];
@@ -1452,7 +1506,7 @@ function useWindowSplitterResizeHandlerBehavior({
1452
1506
  return () => {
1453
1507
  handleElement.removeEventListener("keydown", onKeyDown);
1454
1508
  };
1455
- }, [disabled, handleId, resizeHandler]);
1509
+ }, [panelGroupElement, disabled, handleId, resizeHandler]);
1456
1510
  }
1457
1511
 
1458
1512
  function PanelResizeHandle({
@@ -1485,7 +1539,8 @@ function PanelResizeHandle({
1485
1539
  groupId,
1486
1540
  registerResizeHandle,
1487
1541
  startDragging,
1488
- stopDragging
1542
+ stopDragging,
1543
+ panelGroupElement
1489
1544
  } = panelGroupContext;
1490
1545
  const resizeHandleId = useUniqueId(idFromProps);
1491
1546
  const isDragging = (dragState === null || dragState === void 0 ? void 0 : dragState.dragHandleId) === resizeHandleId;
@@ -1544,7 +1599,8 @@ function PanelResizeHandle({
1544
1599
  useWindowSplitterResizeHandlerBehavior({
1545
1600
  disabled,
1546
1601
  handleId: resizeHandleId,
1547
- resizeHandler
1602
+ resizeHandler,
1603
+ panelGroupElement
1548
1604
  });
1549
1605
  const style = {
1550
1606
  cursor: getCursorStyle(direction),
@@ -1600,52 +1656,16 @@ function PanelResizeHandle({
1600
1656
  }
1601
1657
  PanelResizeHandle.displayName = "PanelResizeHandle";
1602
1658
 
1603
- function calculateAvailablePanelSizeInPixels(groupId) {
1604
- const panelGroupElement = getPanelGroupElement(groupId);
1605
- if (panelGroupElement == null) {
1606
- return NaN;
1607
- }
1608
- const direction = panelGroupElement.getAttribute("data-panel-group-direction");
1609
- const resizeHandles = getResizeHandleElementsForGroup(groupId);
1610
- if (direction === "horizontal") {
1611
- return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
1612
- return accumulated + handle.offsetWidth;
1613
- }, 0);
1614
- } else {
1615
- return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
1616
- return accumulated + handle.offsetHeight;
1617
- }, 0);
1618
- }
1619
- }
1620
-
1621
- function getAvailableGroupSizePixels(groupId) {
1622
- const panelGroupElement = getPanelGroupElement(groupId);
1623
- if (panelGroupElement == null) {
1624
- return NaN;
1625
- }
1626
- const direction = panelGroupElement.getAttribute("data-panel-group-direction");
1627
- const resizeHandles = getResizeHandleElementsForGroup(groupId);
1628
- if (direction === "horizontal") {
1629
- return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
1630
- return accumulated + handle.offsetWidth;
1631
- }, 0);
1632
- } else {
1633
- return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
1634
- return accumulated + handle.offsetHeight;
1635
- }, 0);
1636
- }
1637
- }
1638
-
1639
- function getPanelElement(id) {
1640
- const element = document.querySelector(`[data-panel-id="${id}"]`);
1659
+ function getPanelElement(id, scope = document) {
1660
+ const element = scope.querySelector(`[data-panel-id="${id}"]`);
1641
1661
  if (element) {
1642
1662
  return element;
1643
1663
  }
1644
1664
  return null;
1645
1665
  }
1646
1666
 
1647
- function getPanelElementsForGroup(groupId) {
1648
- return Array.from(document.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
1667
+ function getPanelElementsForGroup(groupId, scope = document) {
1668
+ return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
1649
1669
  }
1650
1670
 
1651
- export { Panel, PanelGroup, PanelResizeHandle, assert, calculateAvailablePanelSizeInPixels, getAvailableGroupSizePixels, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
1671
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@ import { createRef } from "./vendor/react";
12
12
  describe("PanelGroup", () => {
13
13
  let expectedWarnings: string[] = [];
14
14
  let root: Root;
15
+ let container: HTMLElement;
15
16
  let uninstallMockOffsetWidthAndHeight: () => void;
16
17
 
17
18
  function expectWarning(expectedMessage: string) {
@@ -24,7 +25,7 @@ describe("PanelGroup", () => {
24
25
 
25
26
  uninstallMockOffsetWidthAndHeight = mockPanelGroupOffsetWidthAndHeight();
26
27
 
27
- const container = document.createElement("div");
28
+ container = document.createElement("div");
28
29
  document.body.appendChild(container);
29
30
 
30
31
  expectedWarnings = [];
@@ -258,13 +259,147 @@ describe("PanelGroup", () => {
258
259
  );
259
260
  });
260
261
 
261
- const element = getPanelElement("panel");
262
+ const element = getPanelElement("panel", container);
262
263
  assert(element);
263
264
  expect(element.tabIndex).toBe(123);
264
265
  expect(element.getAttribute("data-test-name")).toBe("foo");
265
266
  expect(element.title).toBe("bar");
266
267
  });
267
268
 
269
+ describe("constraints", () => {
270
+ it("should resize a collapsed panel if the collapsedSize prop changes", () => {
271
+ act(() => {
272
+ root.render(
273
+ <PanelGroup direction="horizontal">
274
+ <Panel
275
+ id="left"
276
+ collapsedSize={10}
277
+ collapsible
278
+ defaultSize={10}
279
+ minSize={25}
280
+ />
281
+ <PanelResizeHandle />
282
+ <Panel id="middle" />
283
+ <PanelResizeHandle />
284
+ <Panel
285
+ id="right"
286
+ collapsedSize={10}
287
+ collapsible
288
+ defaultSize={10}
289
+ minSize={25}
290
+ />
291
+ </PanelGroup>
292
+ );
293
+ });
294
+
295
+ let leftElement = getPanelElement("left", container);
296
+ let middleElement = getPanelElement("middle", container);
297
+ let rightElement = getPanelElement("right", container);
298
+ assert(leftElement);
299
+ assert(middleElement);
300
+ assert(rightElement);
301
+ expect(leftElement.getAttribute("data-panel-size")).toBe("10.0");
302
+ expect(middleElement.getAttribute("data-panel-size")).toBe("80.0");
303
+ expect(rightElement.getAttribute("data-panel-size")).toBe("10.0");
304
+
305
+ act(() => {
306
+ root.render(
307
+ <PanelGroup direction="horizontal">
308
+ <Panel id="left" collapsedSize={5} collapsible minSize={25} />
309
+ <PanelResizeHandle />
310
+ <Panel id="middle" />
311
+ <PanelResizeHandle />
312
+ <Panel id="right" collapsedSize={5} collapsible minSize={25} />
313
+ </PanelGroup>
314
+ );
315
+ });
316
+
317
+ expect(leftElement.getAttribute("data-panel-size")).toBe("5.0");
318
+ expect(middleElement.getAttribute("data-panel-size")).toBe("90.0");
319
+ expect(rightElement.getAttribute("data-panel-size")).toBe("5.0");
320
+ });
321
+
322
+ it("should resize a panel if the minSize prop changes", () => {
323
+ act(() => {
324
+ root.render(
325
+ <PanelGroup direction="horizontal">
326
+ <Panel id="left" defaultSize={15} minSize={10} />
327
+ <PanelResizeHandle />
328
+ <Panel id="middle" />
329
+ <PanelResizeHandle />
330
+ <Panel id="right" defaultSize={15} minSize={10} />
331
+ </PanelGroup>
332
+ );
333
+ });
334
+
335
+ let leftElement = getPanelElement("left", container);
336
+ let middleElement = getPanelElement("middle", container);
337
+ let rightElement = getPanelElement("right", container);
338
+ assert(leftElement);
339
+ assert(middleElement);
340
+ assert(rightElement);
341
+ expect(leftElement.getAttribute("data-panel-size")).toBe("15.0");
342
+ expect(middleElement.getAttribute("data-panel-size")).toBe("70.0");
343
+ expect(rightElement.getAttribute("data-panel-size")).toBe("15.0");
344
+
345
+ act(() => {
346
+ root.render(
347
+ <PanelGroup direction="horizontal">
348
+ <Panel id="left" minSize={20} />
349
+ <PanelResizeHandle />
350
+ <Panel id="middle" />
351
+ <PanelResizeHandle />
352
+ <Panel id="right" minSize={20} />
353
+ </PanelGroup>
354
+ );
355
+ });
356
+
357
+ expect(leftElement.getAttribute("data-panel-size")).toBe("20.0");
358
+ expect(middleElement.getAttribute("data-panel-size")).toBe("60.0");
359
+ expect(rightElement.getAttribute("data-panel-size")).toBe("20.0");
360
+ });
361
+
362
+ it("should resize a panel if the maxSize prop changes", () => {
363
+ act(() => {
364
+ root.render(
365
+ <PanelGroup direction="horizontal">
366
+ <Panel id="left" defaultSize={25} maxSize={30} />
367
+ <PanelResizeHandle />
368
+ <Panel id="middle" />
369
+ <PanelResizeHandle />
370
+ <Panel id="right" defaultSize={25} maxSize={30} />
371
+ </PanelGroup>
372
+ );
373
+ });
374
+
375
+ let leftElement = getPanelElement("left", container);
376
+ let middleElement = getPanelElement("middle", container);
377
+ let rightElement = getPanelElement("right", container);
378
+ assert(leftElement);
379
+ assert(middleElement);
380
+ assert(rightElement);
381
+ expect(leftElement.getAttribute("data-panel-size")).toBe("25.0");
382
+ expect(middleElement.getAttribute("data-panel-size")).toBe("50.0");
383
+ expect(rightElement.getAttribute("data-panel-size")).toBe("25.0");
384
+
385
+ act(() => {
386
+ root.render(
387
+ <PanelGroup direction="horizontal">
388
+ <Panel id="left" maxSize={20} />
389
+ <PanelResizeHandle />
390
+ <Panel id="middle" />
391
+ <PanelResizeHandle />
392
+ <Panel id="right" maxSize={20} />
393
+ </PanelGroup>
394
+ );
395
+ });
396
+
397
+ expect(leftElement.getAttribute("data-panel-size")).toBe("20.0");
398
+ expect(middleElement.getAttribute("data-panel-size")).toBe("60.0");
399
+ expect(rightElement.getAttribute("data-panel-size")).toBe("20.0");
400
+ });
401
+ });
402
+
268
403
  describe("callbacks", () => {
269
404
  describe("onCollapse", () => {
270
405
  it("should be called on mount if a panels initial size is 0", () => {
package/src/Panel.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  ForwardedRef,
8
8
  HTMLAttributes,
9
9
  PropsWithChildren,
10
- ReactNode,
10
+ ReactElement,
11
11
  createElement,
12
12
  forwardRef,
13
13
  useContext,
@@ -93,7 +93,7 @@ export function PanelWithForwardedRef({
93
93
  ...rest
94
94
  }: PanelProps & {
95
95
  forwardedRef: ForwardedRef<ImperativePanelHandle>;
96
- }): ReactNode {
96
+ }): ReactElement {
97
97
  const context = useContext(PanelGroupContext);
98
98
  if (context === null) {
99
99
  throw Error(
@@ -108,6 +108,7 @@ export function PanelWithForwardedRef({
108
108
  getPanelStyle,
109
109
  groupId,
110
110
  isPanelCollapsed,
111
+ reevaluatePanelConstraints,
111
112
  registerPanel,
112
113
  resizePanel,
113
114
  unregisterPanel,
@@ -155,6 +156,8 @@ export function PanelWithForwardedRef({
155
156
  useIsomorphicLayoutEffect(() => {
156
157
  const { callbacks, constraints } = panelDataRef.current;
157
158
 
159
+ const prevConstraints = { ...constraints };
160
+
158
161
  panelDataRef.current.id = panelId;
159
162
  panelDataRef.current.idIsFromProps = idFromProps !== undefined;
160
163
  panelDataRef.current.order = order;
@@ -168,6 +171,17 @@ export function PanelWithForwardedRef({
168
171
  constraints.defaultSize = defaultSize;
169
172
  constraints.maxSize = maxSize;
170
173
  constraints.minSize = minSize;
174
+
175
+ // If constraints have changed, we should revisit panel sizes.
176
+ // This is uncommon but may happen if people are trying to implement pixel based constraints.
177
+ if (
178
+ prevConstraints.collapsedSize !== constraints.collapsedSize ||
179
+ prevConstraints.collapsible !== constraints.collapsible ||
180
+ prevConstraints.maxSize !== constraints.maxSize ||
181
+ prevConstraints.minSize !== constraints.minSize
182
+ ) {
183
+ reevaluatePanelConstraints(panelDataRef.current, prevConstraints);
184
+ }
171
185
  });
172
186
 
173
187
  useIsomorphicLayoutEffect(() => {
@@ -15,6 +15,7 @@ import { createRef } from "./vendor/react";
15
15
  describe("PanelGroup", () => {
16
16
  let expectedWarnings: string[] = [];
17
17
  let root: Root;
18
+ let container: HTMLElement;
18
19
  let uninstallMockOffsetWidthAndHeight: () => void;
19
20
 
20
21
  function expectWarning(expectedMessage: string) {
@@ -28,7 +29,7 @@ describe("PanelGroup", () => {
28
29
  // JSDom doesn't support element sizes
29
30
  uninstallMockOffsetWidthAndHeight = mockPanelGroupOffsetWidthAndHeight();
30
31
 
31
- const container = document.createElement("div");
32
+ container = document.createElement("div");
32
33
  document.body.appendChild(container);
33
34
 
34
35
  expectedWarnings = [];
@@ -124,7 +125,7 @@ describe("PanelGroup", () => {
124
125
  );
125
126
  });
126
127
 
127
- const element = getPanelGroupElement("group");
128
+ const element = getPanelGroupElement("group", container);
128
129
  assert(element);
129
130
  expect(element.tabIndex).toBe(123);
130
131
  expect(element.getAttribute("data-test-name")).toBe("foo");