react-resizable-panels 2.0.8 → 2.0.10

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 (34) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/declarations/src/utils/assert.d.ts +1 -1
  3. package/dist/react-resizable-panels.browser.cjs.js +158 -53
  4. package/dist/react-resizable-panels.browser.development.cjs.js +160 -55
  5. package/dist/react-resizable-panels.browser.development.esm.js +159 -54
  6. package/dist/react-resizable-panels.browser.esm.js +157 -52
  7. package/dist/react-resizable-panels.cjs.js +158 -53
  8. package/dist/react-resizable-panels.development.cjs.js +160 -55
  9. package/dist/react-resizable-panels.development.esm.js +159 -54
  10. package/dist/react-resizable-panels.development.node.cjs.js +156 -51
  11. package/dist/react-resizable-panels.development.node.esm.js +155 -50
  12. package/dist/react-resizable-panels.esm.js +157 -52
  13. package/dist/react-resizable-panels.node.cjs.js +154 -49
  14. package/dist/react-resizable-panels.node.esm.js +153 -48
  15. package/package.json +1 -4
  16. package/src/Panel.test.tsx +23 -23
  17. package/src/PanelGroup.test.tsx +32 -3
  18. package/src/PanelGroup.ts +25 -7
  19. package/src/PanelResizeHandle.test.tsx +3 -3
  20. package/src/PanelResizeHandle.ts +1 -1
  21. package/src/PanelResizeHandleRegistry.ts +2 -2
  22. package/src/hooks/useWindowSplitterBehavior.ts +5 -2
  23. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +5 -5
  24. package/src/utils/adjustLayoutByDelta.ts +47 -20
  25. package/src/utils/assert.ts +1 -1
  26. package/src/utils/calculateAriaValues.ts +1 -1
  27. package/src/utils/calculateDragOffsetPercentage.ts +6 -3
  28. package/src/utils/calculateUnsafeDefaultLayout.ts +2 -2
  29. package/src/utils/callPanelCallbacks.ts +1 -1
  30. package/src/utils/resizePanel.ts +4 -1
  31. package/src/utils/test-utils.ts +1 -1
  32. package/src/utils/validatePanelConstraints.ts +4 -1
  33. package/src/utils/validatePanelGroupLayout.ts +3 -3
  34. package/src/vendor/stacking-order.ts +133 -0
@@ -3,7 +3,6 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
- var stackingOrder = require('stacking-order');
7
6
 
8
7
  function _interopNamespace(e) {
9
8
  if (e && e.__esModule) return e;
@@ -268,6 +267,114 @@ function intersects(rectOne, rectTwo, strict) {
268
267
  }
269
268
  }
270
269
 
270
+ // Forked from NPM stacking-order@2.0.0
271
+
272
+ /**
273
+ * Determine which of two nodes appears in front of the other —
274
+ * if `a` is in front, returns 1, otherwise returns -1
275
+ * @param {HTMLElement} a
276
+ * @param {HTMLElement} b
277
+ */
278
+ function compare(a, b) {
279
+ if (a === b) throw new Error("Cannot compare node with itself");
280
+ const ancestors = {
281
+ a: get_ancestors(a),
282
+ b: get_ancestors(b)
283
+ };
284
+ let common_ancestor;
285
+
286
+ // remove shared ancestors
287
+ while (ancestors.a.at(-1) === ancestors.b.at(-1)) {
288
+ a = ancestors.a.pop();
289
+ b = ancestors.b.pop();
290
+ common_ancestor = a;
291
+ }
292
+ assert(common_ancestor, "Stacking order can only be calculated for elements with a common ancestor");
293
+ const z_indexes = {
294
+ a: get_z_index(find_stacking_context(ancestors.a)),
295
+ b: get_z_index(find_stacking_context(ancestors.b))
296
+ };
297
+ if (z_indexes.a === z_indexes.b) {
298
+ const children = common_ancestor.childNodes;
299
+ const furthest_ancestors = {
300
+ a: ancestors.a.at(-1),
301
+ b: ancestors.b.at(-1)
302
+ };
303
+ let i = children.length;
304
+ while (i--) {
305
+ const child = children[i];
306
+ if (child === furthest_ancestors.a) return 1;
307
+ if (child === furthest_ancestors.b) return -1;
308
+ }
309
+ }
310
+ return Math.sign(z_indexes.a - z_indexes.b);
311
+ }
312
+ const props = /\b(?:position|zIndex|opacity|transform|webkitTransform|mixBlendMode|filter|webkitFilter|isolation)\b/;
313
+
314
+ /** @param {HTMLElement} node */
315
+ function is_flex_item(node) {
316
+ const display = getComputedStyle(get_parent(node)).display;
317
+ return display === "flex" || display === "inline-flex";
318
+ }
319
+
320
+ /** @param {HTMLElement} node */
321
+ function creates_stacking_context(node) {
322
+ const style = getComputedStyle(node);
323
+
324
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
325
+ if (style.position === "fixed") return true;
326
+ // Forked to fix upstream bug https://github.com/Rich-Harris/stacking-order/issues/3
327
+ // if (
328
+ // (style.zIndex !== "auto" && style.position !== "static") ||
329
+ // is_flex_item(node)
330
+ // )
331
+ if (style.zIndex !== "auto" && (style.position !== "static" || is_flex_item(node))) return true;
332
+ if (+style.opacity < 1) return true;
333
+ if ("transform" in style && style.transform !== "none") return true;
334
+ if ("webkitTransform" in style && style.webkitTransform !== "none") return true;
335
+ if ("mixBlendMode" in style && style.mixBlendMode !== "normal") return true;
336
+ if ("filter" in style && style.filter !== "none") return true;
337
+ if ("webkitFilter" in style && style.webkitFilter !== "none") return true;
338
+ if ("isolation" in style && style.isolation === "isolate") return true;
339
+ if (props.test(style.willChange)) return true;
340
+ // @ts-expect-error
341
+ if (style.webkitOverflowScrolling === "touch") return true;
342
+ return false;
343
+ }
344
+
345
+ /** @param {HTMLElement[]} nodes */
346
+ function find_stacking_context(nodes) {
347
+ let i = nodes.length;
348
+ while (i--) {
349
+ const node = nodes[i];
350
+ assert(node, "Missing node");
351
+ if (creates_stacking_context(node)) return node;
352
+ }
353
+ return null;
354
+ }
355
+
356
+ /** @param {HTMLElement} node */
357
+ function get_z_index(node) {
358
+ return node && Number(getComputedStyle(node).zIndex) || 0;
359
+ }
360
+
361
+ /** @param {HTMLElement} node */
362
+ function get_ancestors(node) {
363
+ const ancestors = [];
364
+ while (node) {
365
+ ancestors.push(node);
366
+ node = get_parent(node);
367
+ }
368
+ return ancestors; // [ node, ... <body>, <html>, document ]
369
+ }
370
+
371
+ /** @param {HTMLElement} node */
372
+ function get_parent(node) {
373
+ var _node$parentNode;
374
+ // @ts-ignore
375
+ return ((_node$parentNode = node.parentNode) === null || _node$parentNode === void 0 ? void 0 : _node$parentNode.host) || node.parentNode;
376
+ }
377
+
271
378
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
272
379
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
273
380
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -409,7 +516,7 @@ function recalculateIntersectingHandles({
409
516
  // Calculating stacking order has a cost, so we should avoid it if possible
410
517
  // That is why we only check potentially intersecting handles,
411
518
  // and why we skip if the event target is within the handle's DOM
412
- stackingOrder.compare(targetElement, dragHandleElement) > 0) {
519
+ compare(targetElement, dragHandleElement) > 0) {
413
520
  // If the target is above the drag handle, then we also need to confirm they overlap
414
521
  // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
415
522
  //
@@ -480,7 +587,7 @@ function updateListeners() {
480
587
  window.removeEventListener("mouseup", handlePointerUp);
481
588
  window.removeEventListener("touchcancel", handlePointerUp);
482
589
  window.removeEventListener("touchend", handlePointerUp);
483
- if (registerResizeHandle.length > 0) {
590
+ if (registeredResizeHandlers.size > 0) {
484
591
  if (isPointerDown) {
485
592
  if (intersectingHandles.length > 0) {
486
593
  ownerDocumentCounts.forEach((count, ownerDocument) => {
@@ -527,7 +634,7 @@ function updateResizeHandlerStates(action, event) {
527
634
  });
528
635
  }
529
636
 
530
- function assert(expectedCondition, message = "Assertion failed!") {
637
+ function assert(expectedCondition, message) {
531
638
  if (!expectedCondition) {
532
639
  console.error(message);
533
640
  throw Error(message);
@@ -558,7 +665,7 @@ function resizePanel({
558
665
  size
559
666
  }) {
560
667
  const panelConstraints = panelConstraintsArray[panelIndex];
561
- assert(panelConstraints != null);
668
+ assert(panelConstraints != null, `Panel constraints not found for index ${panelIndex}`);
562
669
  let {
563
670
  collapsedSize = 0,
564
671
  collapsible,
@@ -596,8 +703,8 @@ function adjustLayoutByDelta({
596
703
  }
597
704
  const nextLayout = [...prevLayout];
598
705
  const [firstPivotIndex, secondPivotIndex] = pivotIndices;
599
- assert(firstPivotIndex != null);
600
- assert(secondPivotIndex != null);
706
+ assert(firstPivotIndex != null, "Invalid first pivot index");
707
+ assert(secondPivotIndex != null, "Invalid second pivot index");
601
708
  let deltaApplied = 0;
602
709
 
603
710
  //const DEBUG = [];
@@ -623,19 +730,18 @@ function adjustLayoutByDelta({
623
730
  // Check if we should expand a collapsed panel
624
731
  const index = delta < 0 ? secondPivotIndex : firstPivotIndex;
625
732
  const panelConstraints = panelConstraintsArray[index];
626
- assert(panelConstraints);
733
+ assert(panelConstraints, `Panel constraints not found for index ${index}`);
734
+ const {
735
+ collapsedSize = 0,
736
+ collapsible,
737
+ minSize = 0
738
+ } = panelConstraints;
627
739
 
628
740
  //DEBUG.push(`edge case check 1: ${index}`);
629
741
  //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
630
- if (panelConstraints.collapsible) {
742
+ if (collapsible) {
631
743
  const prevSize = prevLayout[index];
632
- assert(prevSize != null);
633
- const panelConstraints = panelConstraintsArray[index];
634
- assert(panelConstraints);
635
- const {
636
- collapsedSize = 0,
637
- minSize = 0
638
- } = panelConstraints;
744
+ assert(prevSize != null, `Previous layout not found for panel index ${index}`);
639
745
  if (fuzzyNumbersEqual(prevSize, collapsedSize)) {
640
746
  const localDelta = minSize - prevSize;
641
747
  //DEBUG.push(` -> expand delta: ${localDelta}`);
@@ -652,22 +758,18 @@ function adjustLayoutByDelta({
652
758
  // Check if we should collapse a panel at its minimum size
653
759
  const index = delta < 0 ? firstPivotIndex : secondPivotIndex;
654
760
  const panelConstraints = panelConstraintsArray[index];
655
- assert(panelConstraints);
761
+ assert(panelConstraints, `No panel constraints found for index ${index}`);
656
762
  const {
657
- collapsible
763
+ collapsedSize = 0,
764
+ collapsible,
765
+ minSize = 0
658
766
  } = panelConstraints;
659
767
 
660
768
  //DEBUG.push(`edge case check 2: ${index}`);
661
769
  //DEBUG.push(` -> collapsible? ${collapsible}`);
662
770
  if (collapsible) {
663
771
  const prevSize = prevLayout[index];
664
- assert(prevSize != null);
665
- const panelConstraints = panelConstraintsArray[index];
666
- assert(panelConstraints);
667
- const {
668
- collapsedSize = 0,
669
- minSize = 0
670
- } = panelConstraints;
772
+ assert(prevSize != null, `Previous layout not found for panel index ${index}`);
671
773
  if (fuzzyNumbersEqual(prevSize, minSize)) {
672
774
  const localDelta = prevSize - collapsedSize;
673
775
  //DEBUG.push(` -> expand delta: ${localDelta}`);
@@ -697,7 +799,7 @@ function adjustLayoutByDelta({
697
799
  //DEBUG.push("pre calc...");
698
800
  while (true) {
699
801
  const prevSize = prevLayout[index];
700
- assert(prevSize != null);
802
+ assert(prevSize != null, `Previous layout not found for panel index ${index}`);
701
803
  const maxSafeSize = resizePanel({
702
804
  panelConstraints: panelConstraintsArray,
703
805
  panelIndex: index,
@@ -728,7 +830,7 @@ function adjustLayoutByDelta({
728
830
  while (index >= 0 && index < panelConstraintsArray.length) {
729
831
  const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
730
832
  const prevSize = prevLayout[index];
731
- assert(prevSize != null);
833
+ assert(prevSize != null, `Previous layout not found for panel index ${index}`);
732
834
  const unsafeSize = prevSize - deltaRemaining;
733
835
  const safeSize = resizePanel({
734
836
  panelConstraints: panelConstraintsArray,
@@ -765,7 +867,7 @@ function adjustLayoutByDelta({
765
867
  // Now distribute the applied delta to the panels in the other direction
766
868
  const pivotIndex = delta < 0 ? secondPivotIndex : firstPivotIndex;
767
869
  const prevSize = prevLayout[pivotIndex];
768
- assert(prevSize != null);
870
+ assert(prevSize != null, `Previous layout not found for panel index ${pivotIndex}`);
769
871
  const unsafeSize = prevSize + deltaApplied;
770
872
  const safeSize = resizePanel({
771
873
  panelConstraints: panelConstraintsArray,
@@ -783,7 +885,7 @@ function adjustLayoutByDelta({
783
885
  let index = pivotIndex;
784
886
  while (index >= 0 && index < panelConstraintsArray.length) {
785
887
  const prevSize = nextLayout[index];
786
- assert(prevSize != null);
888
+ assert(prevSize != null, `Previous layout not found for panel index ${index}`);
787
889
  const unsafeSize = prevSize + deltaRemaining;
788
890
  const safeSize = resizePanel({
789
891
  panelConstraints: panelConstraintsArray,
@@ -886,17 +988,17 @@ function useWindowSplitterPanelGroupBehavior({
886
988
  return;
887
989
  }
888
990
  const eagerValues = eagerValuesRef.current;
889
- assert(eagerValues);
991
+ assert(eagerValues, `Eager values not found`);
890
992
  const {
891
993
  panelDataArray
892
994
  } = eagerValues;
893
995
  const groupElement = getPanelGroupElement(groupId, panelGroupElement);
894
996
  assert(groupElement != null, `No group found for id "${groupId}"`);
895
997
  const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
896
- assert(handles);
998
+ assert(handles, `No resize handles found for group id "${groupId}"`);
897
999
  const cleanupFunctions = handles.map(handle => {
898
1000
  const handleId = handle.getAttribute("data-panel-resize-handle-id");
899
- assert(handleId);
1001
+ assert(handleId, `Resize handle element has no handle id attribute`);
900
1002
  const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelDataArray, panelGroupElement);
901
1003
  if (idBefore == null || idAfter == null) {
902
1004
  return () => {};
@@ -912,7 +1014,7 @@ function useWindowSplitterPanelGroupBehavior({
912
1014
  const index = panelDataArray.findIndex(panelData => panelData.id === idBefore);
913
1015
  if (index >= 0) {
914
1016
  const panelData = panelDataArray[index];
915
- assert(panelData);
1017
+ assert(panelData, `No panel data found for index ${index}`);
916
1018
  const size = layout[index];
917
1019
  const {
918
1020
  collapsedSize = 0,
@@ -971,15 +1073,15 @@ function getResizeEventCursorPosition(direction, event) {
971
1073
  function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState, panelGroupElement) {
972
1074
  const isHorizontal = direction === "horizontal";
973
1075
  const handleElement = getResizeHandleElement(dragHandleId, panelGroupElement);
974
- assert(handleElement);
1076
+ assert(handleElement, `No resize handle element found for id "${dragHandleId}"`);
975
1077
  const groupId = handleElement.getAttribute("data-panel-group-id");
976
- assert(groupId);
1078
+ assert(groupId, `Resize handle element has no group id attribute`);
977
1079
  let {
978
1080
  initialCursorPosition
979
1081
  } = initialDragState;
980
1082
  const cursorPosition = getResizeEventCursorPosition(direction, event);
981
1083
  const groupElement = getPanelGroupElement(groupId, panelGroupElement);
982
- assert(groupElement);
1084
+ assert(groupElement, `No group element found for id "${groupId}"`);
983
1085
  const groupRect = groupElement.getBoundingClientRect();
984
1086
  const groupSizeInPixels = isHorizontal ? groupRect.width : groupRect.height;
985
1087
  const offsetPixels = cursorPosition - initialCursorPosition;
@@ -1033,7 +1135,7 @@ function calculateDeltaPercentage(event, dragHandleId, direction, initialDragSta
1033
1135
  function callPanelCallbacks(panelsArray, layout, panelIdToLastNotifiedSizeMap) {
1034
1136
  layout.forEach((size, index) => {
1035
1137
  const panelData = panelsArray[index];
1036
- assert(panelData);
1138
+ assert(panelData, `Panel data not found for index ${index}`);
1037
1139
  const {
1038
1140
  callbacks,
1039
1141
  constraints,
@@ -1217,7 +1319,7 @@ function validatePanelGroupLayout({
1217
1319
  } else if (!fuzzyNumbersEqual(nextLayoutTotalSize, 100)) {
1218
1320
  for (let index = 0; index < panelConstraints.length; index++) {
1219
1321
  const unsafeSize = nextLayout[index];
1220
- assert(unsafeSize != null);
1322
+ assert(unsafeSize != null, `No layout data found for index ${index}`);
1221
1323
  const safeSize = 100 / nextLayoutTotalSize * unsafeSize;
1222
1324
  nextLayout[index] = safeSize;
1223
1325
  }
@@ -1227,7 +1329,7 @@ function validatePanelGroupLayout({
1227
1329
  // First pass: Validate the proposed layout given each panel's constraints
1228
1330
  for (let index = 0; index < panelConstraints.length; index++) {
1229
1331
  const unsafeSize = nextLayout[index];
1230
- assert(unsafeSize != null);
1332
+ assert(unsafeSize != null, `No layout data found for index ${index}`);
1231
1333
  const safeSize = resizePanel({
1232
1334
  panelConstraints,
1233
1335
  panelIndex: index,
@@ -1244,7 +1346,7 @@ function validatePanelGroupLayout({
1244
1346
  if (!fuzzyNumbersEqual(remainingSize, 0)) {
1245
1347
  for (let index = 0; index < panelConstraints.length; index++) {
1246
1348
  const prevSize = nextLayout[index];
1247
- assert(prevSize != null);
1349
+ assert(prevSize != null, `No layout data found for index ${index}`);
1248
1350
  const unsafeSize = prevSize + remainingSize;
1249
1351
  const safeSize = resizePanel({
1250
1352
  panelConstraints,
@@ -1402,7 +1504,7 @@ function PanelGroupWithForwardedRef({
1402
1504
  panelSize,
1403
1505
  pivotIndices
1404
1506
  } = panelDataHelper(panelDataArray, panelData, prevLayout);
1405
- assert(panelSize != null);
1507
+ assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
1406
1508
  if (panelSize !== collapsedSize) {
1407
1509
  // Store size before collapse;
1408
1510
  // This is the size that gets restored if the expand() API is used.
@@ -1479,7 +1581,7 @@ function PanelGroupWithForwardedRef({
1479
1581
  const {
1480
1582
  panelSize
1481
1583
  } = panelDataHelper(panelDataArray, panelData, layout);
1482
- assert(panelSize != null);
1584
+ assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
1483
1585
  return panelSize;
1484
1586
  }, []);
1485
1587
 
@@ -1523,7 +1625,7 @@ function PanelGroupWithForwardedRef({
1523
1625
  collapsible,
1524
1626
  panelSize
1525
1627
  } = panelDataHelper(panelDataArray, panelData, layout);
1526
- assert(panelSize != null);
1628
+ assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
1527
1629
  return !collapsible || panelSize > collapsedSize;
1528
1630
  }, []);
1529
1631
  const registerPanel = useCallback(panelData => {
@@ -1634,7 +1736,7 @@ function PanelGroupWithForwardedRef({
1634
1736
  panelSize,
1635
1737
  pivotIndices
1636
1738
  } = panelDataHelper(panelDataArray, panelData, prevLayout);
1637
- assert(panelSize != null);
1739
+ assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
1638
1740
  const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1639
1741
  const delta = isLastPanel ? panelSize - unsafePanelSize : unsafePanelSize - panelSize;
1640
1742
  const nextLayout = adjustLayoutByDelta({
@@ -1671,7 +1773,10 @@ function PanelGroupWithForwardedRef({
1671
1773
  const {
1672
1774
  panelSize: prevPanelSize
1673
1775
  } = panelDataHelper(panelDataArray, panelData, layout);
1674
- assert(prevPanelSize != null);
1776
+ if (prevPanelSize == null) {
1777
+ // It's possible that the panels in this group have changed since the last render
1778
+ return;
1779
+ }
1675
1780
  if (prevCollapsible && nextCollapsible && prevPanelSize === prevCollapsedSize) {
1676
1781
  if (prevCollapsedSize !== nextCollapsedSize) {
1677
1782
  resizePanel(panelData, nextCollapsedSize);
@@ -1693,7 +1798,7 @@ function PanelGroupWithForwardedRef({
1693
1798
  return;
1694
1799
  }
1695
1800
  const handleElement = getResizeHandleElement(dragHandleId, panelGroupElementRef.current);
1696
- assert(handleElement);
1801
+ assert(handleElement, `Drag handle element not found for id "${dragHandleId}"`);
1697
1802
  const initialCursorPosition = getResizeEventCursorPosition(direction, event);
1698
1803
  setDragState({
1699
1804
  dragHandleId,
@@ -1822,10 +1927,10 @@ function useWindowSplitterResizeHandlerBehavior({
1822
1927
  {
1823
1928
  event.preventDefault();
1824
1929
  const groupId = handleElement.getAttribute("data-panel-group-id");
1825
- assert(groupId);
1930
+ assert(groupId, `No group element found for id "${groupId}"`);
1826
1931
  const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
1827
1932
  const index = getResizeHandleElementIndex(groupId, handleId, panelGroupElement);
1828
- assert(index !== null);
1933
+ assert(index !== null, `No resize element found for id "${handleId}"`);
1829
1934
  const nextIndex = event.shiftKey ? index > 0 ? index - 1 : handles.length - 1 : index + 1 < handles.length ? index + 1 : 0;
1830
1935
  const nextHandle = handles[nextIndex];
1831
1936
  nextHandle.focus();
@@ -1894,7 +1999,7 @@ function PanelResizeHandle({
1894
1999
  return;
1895
2000
  }
1896
2001
  const element = elementRef.current;
1897
- assert(element);
2002
+ assert(element, "Element ref not attached");
1898
2003
  const setResizeHandlerState = (action, isActive, event) => {
1899
2004
  if (isActive) {
1900
2005
  switch (action) {