react-resizable-panels 2.0.4 → 2.0.6

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 (35) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/declarations/src/index.d.ts +3 -1
  3. package/dist/declarations/src/utils/rects/getIntersectingRectangle.d.ts +2 -0
  4. package/dist/declarations/src/utils/rects/intersects.d.ts +2 -0
  5. package/dist/declarations/src/utils/rects/types.d.ts +6 -0
  6. package/dist/declarations/src/vendor/react.d.ts +3 -2
  7. package/dist/react-resizable-panels.browser.cjs.js +87 -7
  8. package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
  9. package/dist/react-resizable-panels.browser.development.cjs.js +87 -7
  10. package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
  11. package/dist/react-resizable-panels.browser.development.esm.js +86 -8
  12. package/dist/react-resizable-panels.browser.esm.js +86 -8
  13. package/dist/react-resizable-panels.cjs.js +87 -7
  14. package/dist/react-resizable-panels.cjs.mjs +3 -1
  15. package/dist/react-resizable-panels.development.cjs.js +87 -7
  16. package/dist/react-resizable-panels.development.cjs.mjs +3 -1
  17. package/dist/react-resizable-panels.development.esm.js +86 -8
  18. package/dist/react-resizable-panels.development.node.cjs.js +84 -8
  19. package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
  20. package/dist/react-resizable-panels.development.node.esm.js +83 -9
  21. package/dist/react-resizable-panels.esm.js +86 -8
  22. package/dist/react-resizable-panels.node.cjs.js +84 -8
  23. package/dist/react-resizable-panels.node.cjs.mjs +3 -1
  24. package/dist/react-resizable-panels.node.esm.js +83 -9
  25. package/package.json +4 -1
  26. package/src/PanelResizeHandle.ts +2 -2
  27. package/src/PanelResizeHandleRegistry.ts +75 -8
  28. package/src/hooks/useIsomorphicEffect.ts +4 -2
  29. package/src/index.ts +4 -0
  30. package/src/utils/rects/getIntersectingRectangle.test.ts +198 -0
  31. package/src/utils/rects/getIntersectingRectangle.ts +28 -0
  32. package/src/utils/rects/intersects.test.ts +197 -0
  33. package/src/utils/rects/intersects.ts +23 -0
  34. package/src/utils/rects/types.ts +6 -0
  35. package/src/vendor/react.ts +3 -1
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { compare } from 'stacking-order';
2
3
 
3
4
  const isBrowser = typeof window !== "undefined";
4
5
 
@@ -23,11 +24,12 @@ const {
23
24
 
24
25
  // `toString()` prevents bundlers from trying to `import { useId } from 'react'`
25
26
  const useId = React["useId".toString()];
27
+ const useLayoutEffect_do_not_use_directly = useLayoutEffect;
26
28
 
27
29
  const PanelGroupContext = createContext(null);
28
30
  PanelGroupContext.displayName = "PanelGroupContext";
29
31
 
30
- const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : () => {};
32
+ const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect_do_not_use_directly : () => {};
31
33
 
32
34
  const wrappedUseId = typeof useId === "function" ? useId : () => null;
33
35
  let counter = 0;
@@ -272,6 +274,14 @@ function getInputType() {
272
274
  }
273
275
  }
274
276
 
277
+ function intersects(rectOne, rectTwo, strict) {
278
+ if (strict) {
279
+ return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
280
+ } else {
281
+ return rectOne.x <= rectTwo.x + rectTwo.width && rectOne.x + rectOne.width >= rectTwo.x && rectOne.y <= rectTwo.y + rectTwo.height && rectOne.y + rectOne.height >= rectTwo.y;
282
+ }
283
+ }
284
+
275
285
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
276
286
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
277
287
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -310,12 +320,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
310
320
  };
311
321
  }
312
322
  function handlePointerDown(event) {
323
+ const {
324
+ target
325
+ } = event;
313
326
  const {
314
327
  x,
315
328
  y
316
329
  } = getResizeEventCoordinates(event);
317
330
  isPointerDown = true;
318
331
  recalculateIntersectingHandles({
332
+ target,
319
333
  x,
320
334
  y
321
335
  });
@@ -331,10 +345,15 @@ function handlePointerMove(event) {
331
345
  y
332
346
  } = getResizeEventCoordinates(event);
333
347
  if (!isPointerDown) {
348
+ const {
349
+ target
350
+ } = event;
351
+
334
352
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
335
353
  // at that point, the handles may not move with the pointer (depending on constraints)
336
354
  // but the same set of active handles should be locked until the pointer is released
337
355
  recalculateIntersectingHandles({
356
+ target,
338
357
  x,
339
358
  y
340
359
  });
@@ -348,6 +367,9 @@ function handlePointerMove(event) {
348
367
  }
349
368
  }
350
369
  function handlePointerUp(event) {
370
+ const {
371
+ target
372
+ } = event;
351
373
  const {
352
374
  x,
353
375
  y
@@ -357,33 +379,72 @@ function handlePointerUp(event) {
357
379
  if (intersectingHandles.length > 0) {
358
380
  event.preventDefault();
359
381
  }
382
+ updateResizeHandlerStates("up", event);
360
383
  recalculateIntersectingHandles({
384
+ target,
361
385
  x,
362
386
  y
363
387
  });
364
- updateResizeHandlerStates("up", event);
365
388
  updateCursor();
366
389
  updateListeners();
367
390
  }
368
391
  function recalculateIntersectingHandles({
392
+ target,
369
393
  x,
370
394
  y
371
395
  }) {
372
396
  intersectingHandles.splice(0);
397
+ let targetElement = null;
398
+ if (target instanceof HTMLElement) {
399
+ targetElement = target;
400
+ }
373
401
  registeredResizeHandlers.forEach(data => {
374
402
  const {
375
- element,
403
+ element: dragHandleElement,
376
404
  hitAreaMargins
377
405
  } = data;
406
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
378
407
  const {
379
408
  bottom,
380
409
  left,
381
410
  right,
382
411
  top
383
- } = element.getBoundingClientRect();
412
+ } = dragHandleRect;
384
413
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
385
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
386
- if (intersects) {
414
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
415
+ if (eventIntersects) {
416
+ // TRICKY
417
+ // We listen for pointers events at the root in order to support hit area margins
418
+ // (determining when the pointer is close enough to an element to be considered a "hit")
419
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
420
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
421
+ // and the element that was actually clicked/touched
422
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
423
+ // Calculating stacking order has a cost, so we should avoid it if possible
424
+ // That is why we only check potentially intersecting handles,
425
+ // and why we skip if the event target is within the handle's DOM
426
+ compare(targetElement, dragHandleElement) > 0) {
427
+ // If the target is above the drag handle, then we also need to confirm they overlap
428
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
429
+ //
430
+ // It's not enough to compare only the target
431
+ // The target might be a small element inside of a larger container
432
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
433
+ let currentElement = targetElement;
434
+ let didIntersect = false;
435
+ while (currentElement) {
436
+ if (currentElement.contains(dragHandleElement)) {
437
+ break;
438
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
439
+ didIntersect = true;
440
+ break;
441
+ }
442
+ currentElement = currentElement.parentElement;
443
+ }
444
+ if (didIntersect) {
445
+ return;
446
+ }
447
+ }
387
448
  intersectingHandles.push(data);
388
449
  }
389
450
  });
@@ -2016,7 +2077,7 @@ function PanelResizeHandle({
2016
2077
  const committedValuesRef = useRef({
2017
2078
  state
2018
2079
  });
2019
- useLayoutEffect(() => {
2080
+ useIsomorphicLayoutEffect(() => {
2020
2081
  committedValuesRef.current.state = state;
2021
2082
  });
2022
2083
  useEffect(() => {
@@ -2131,4 +2192,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
2131
2192
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2132
2193
  }
2133
2194
 
2134
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
2195
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2196
+ if (!intersects(rectOne, rectTwo, strict)) {
2197
+ return {
2198
+ x: 0,
2199
+ y: 0,
2200
+ width: 0,
2201
+ height: 0
2202
+ };
2203
+ }
2204
+ return {
2205
+ x: Math.max(rectOne.x, rectTwo.x),
2206
+ y: Math.max(rectOne.y, rectTwo.y),
2207
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2208
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2209
+ };
2210
+ }
2211
+
2212
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, intersects };
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
+ var stackingOrder = require('stacking-order');
6
7
 
7
8
  function _interopNamespace(e) {
8
9
  if (e && e.__esModule) return e;
@@ -259,6 +260,14 @@ function getInputType() {
259
260
  }
260
261
  }
261
262
 
263
+ function intersects(rectOne, rectTwo, strict) {
264
+ if (strict) {
265
+ return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
266
+ } else {
267
+ return rectOne.x <= rectTwo.x + rectTwo.width && rectOne.x + rectOne.width >= rectTwo.x && rectOne.y <= rectTwo.y + rectTwo.height && rectOne.y + rectOne.height >= rectTwo.y;
268
+ }
269
+ }
270
+
262
271
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
263
272
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
264
273
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -297,12 +306,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
297
306
  };
298
307
  }
299
308
  function handlePointerDown(event) {
309
+ const {
310
+ target
311
+ } = event;
300
312
  const {
301
313
  x,
302
314
  y
303
315
  } = getResizeEventCoordinates(event);
304
316
  isPointerDown = true;
305
317
  recalculateIntersectingHandles({
318
+ target,
306
319
  x,
307
320
  y
308
321
  });
@@ -318,10 +331,15 @@ function handlePointerMove(event) {
318
331
  y
319
332
  } = getResizeEventCoordinates(event);
320
333
  if (!isPointerDown) {
334
+ const {
335
+ target
336
+ } = event;
337
+
321
338
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
322
339
  // at that point, the handles may not move with the pointer (depending on constraints)
323
340
  // but the same set of active handles should be locked until the pointer is released
324
341
  recalculateIntersectingHandles({
342
+ target,
325
343
  x,
326
344
  y
327
345
  });
@@ -335,6 +353,9 @@ function handlePointerMove(event) {
335
353
  }
336
354
  }
337
355
  function handlePointerUp(event) {
356
+ const {
357
+ target
358
+ } = event;
338
359
  const {
339
360
  x,
340
361
  y
@@ -344,33 +365,72 @@ function handlePointerUp(event) {
344
365
  if (intersectingHandles.length > 0) {
345
366
  event.preventDefault();
346
367
  }
368
+ updateResizeHandlerStates("up", event);
347
369
  recalculateIntersectingHandles({
370
+ target,
348
371
  x,
349
372
  y
350
373
  });
351
- updateResizeHandlerStates("up", event);
352
374
  updateCursor();
353
375
  updateListeners();
354
376
  }
355
377
  function recalculateIntersectingHandles({
378
+ target,
356
379
  x,
357
380
  y
358
381
  }) {
359
382
  intersectingHandles.splice(0);
383
+ let targetElement = null;
384
+ if (target instanceof HTMLElement) {
385
+ targetElement = target;
386
+ }
360
387
  registeredResizeHandlers.forEach(data => {
361
388
  const {
362
- element,
389
+ element: dragHandleElement,
363
390
  hitAreaMargins
364
391
  } = data;
392
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
365
393
  const {
366
394
  bottom,
367
395
  left,
368
396
  right,
369
397
  top
370
- } = element.getBoundingClientRect();
398
+ } = dragHandleRect;
371
399
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
372
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
373
- if (intersects) {
400
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
401
+ if (eventIntersects) {
402
+ // TRICKY
403
+ // We listen for pointers events at the root in order to support hit area margins
404
+ // (determining when the pointer is close enough to an element to be considered a "hit")
405
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
406
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
407
+ // and the element that was actually clicked/touched
408
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
409
+ // Calculating stacking order has a cost, so we should avoid it if possible
410
+ // That is why we only check potentially intersecting handles,
411
+ // and why we skip if the event target is within the handle's DOM
412
+ stackingOrder.compare(targetElement, dragHandleElement) > 0) {
413
+ // If the target is above the drag handle, then we also need to confirm they overlap
414
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
415
+ //
416
+ // It's not enough to compare only the target
417
+ // The target might be a small element inside of a larger container
418
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
419
+ let currentElement = targetElement;
420
+ let didIntersect = false;
421
+ while (currentElement) {
422
+ if (currentElement.contains(dragHandleElement)) {
423
+ break;
424
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
425
+ didIntersect = true;
426
+ break;
427
+ }
428
+ currentElement = currentElement.parentElement;
429
+ }
430
+ if (didIntersect) {
431
+ return;
432
+ }
433
+ }
374
434
  intersectingHandles.push(data);
375
435
  }
376
436
  });
@@ -1819,9 +1879,6 @@ function PanelResizeHandle({
1819
1879
  const committedValuesRef = useRef({
1820
1880
  state
1821
1881
  });
1822
- useLayoutEffect(() => {
1823
- committedValuesRef.current.state = state;
1824
- });
1825
1882
  useEffect(() => {
1826
1883
  if (disabled) {
1827
1884
  setResizeHandler(null);
@@ -1934,10 +1991,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
1934
1991
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
1935
1992
  }
1936
1993
 
1994
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
1995
+ if (!intersects(rectOne, rectTwo, strict)) {
1996
+ return {
1997
+ x: 0,
1998
+ y: 0,
1999
+ width: 0,
2000
+ height: 0
2001
+ };
2002
+ }
2003
+ return {
2004
+ x: Math.max(rectOne.x, rectTwo.x),
2005
+ y: Math.max(rectOne.y, rectTwo.y),
2006
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2007
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2008
+ };
2009
+ }
2010
+
1937
2011
  exports.Panel = Panel;
1938
2012
  exports.PanelGroup = PanelGroup;
1939
2013
  exports.PanelResizeHandle = PanelResizeHandle;
1940
2014
  exports.assert = assert;
2015
+ exports.getIntersectingRectangle = getIntersectingRectangle;
1941
2016
  exports.getPanelElement = getPanelElement;
1942
2017
  exports.getPanelElementsForGroup = getPanelElementsForGroup;
1943
2018
  exports.getPanelGroupElement = getPanelGroupElement;
@@ -1945,3 +2020,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
1945
2020
  exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
1946
2021
  exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
1947
2022
  exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
2023
+ exports.intersects = intersects;
@@ -3,11 +3,13 @@ export {
3
3
  PanelGroup,
4
4
  PanelResizeHandle,
5
5
  assert,
6
+ getIntersectingRectangle,
6
7
  getPanelElement,
7
8
  getPanelElementsForGroup,
8
9
  getPanelGroupElement,
9
10
  getResizeHandleElement,
10
11
  getResizeHandleElementIndex,
11
12
  getResizeHandleElementsForGroup,
12
- getResizeHandlePanelIds
13
+ getResizeHandlePanelIds,
14
+ intersects
13
15
  } from "./react-resizable-panels.node.cjs.js";
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { compare } from 'stacking-order';
2
3
 
3
4
  // This module exists to work around Webpack issue https://github.com/webpack/webpack/issues/14814
4
5
 
@@ -235,6 +236,14 @@ function getInputType() {
235
236
  }
236
237
  }
237
238
 
239
+ function intersects(rectOne, rectTwo, strict) {
240
+ if (strict) {
241
+ return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
242
+ } else {
243
+ return rectOne.x <= rectTwo.x + rectTwo.width && rectOne.x + rectOne.width >= rectTwo.x && rectOne.y <= rectTwo.y + rectTwo.height && rectOne.y + rectOne.height >= rectTwo.y;
244
+ }
245
+ }
246
+
238
247
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
239
248
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
240
249
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -273,12 +282,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
273
282
  };
274
283
  }
275
284
  function handlePointerDown(event) {
285
+ const {
286
+ target
287
+ } = event;
276
288
  const {
277
289
  x,
278
290
  y
279
291
  } = getResizeEventCoordinates(event);
280
292
  isPointerDown = true;
281
293
  recalculateIntersectingHandles({
294
+ target,
282
295
  x,
283
296
  y
284
297
  });
@@ -294,10 +307,15 @@ function handlePointerMove(event) {
294
307
  y
295
308
  } = getResizeEventCoordinates(event);
296
309
  if (!isPointerDown) {
310
+ const {
311
+ target
312
+ } = event;
313
+
297
314
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
298
315
  // at that point, the handles may not move with the pointer (depending on constraints)
299
316
  // but the same set of active handles should be locked until the pointer is released
300
317
  recalculateIntersectingHandles({
318
+ target,
301
319
  x,
302
320
  y
303
321
  });
@@ -311,6 +329,9 @@ function handlePointerMove(event) {
311
329
  }
312
330
  }
313
331
  function handlePointerUp(event) {
332
+ const {
333
+ target
334
+ } = event;
314
335
  const {
315
336
  x,
316
337
  y
@@ -320,33 +341,72 @@ function handlePointerUp(event) {
320
341
  if (intersectingHandles.length > 0) {
321
342
  event.preventDefault();
322
343
  }
344
+ updateResizeHandlerStates("up", event);
323
345
  recalculateIntersectingHandles({
346
+ target,
324
347
  x,
325
348
  y
326
349
  });
327
- updateResizeHandlerStates("up", event);
328
350
  updateCursor();
329
351
  updateListeners();
330
352
  }
331
353
  function recalculateIntersectingHandles({
354
+ target,
332
355
  x,
333
356
  y
334
357
  }) {
335
358
  intersectingHandles.splice(0);
359
+ let targetElement = null;
360
+ if (target instanceof HTMLElement) {
361
+ targetElement = target;
362
+ }
336
363
  registeredResizeHandlers.forEach(data => {
337
364
  const {
338
- element,
365
+ element: dragHandleElement,
339
366
  hitAreaMargins
340
367
  } = data;
368
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
341
369
  const {
342
370
  bottom,
343
371
  left,
344
372
  right,
345
373
  top
346
- } = element.getBoundingClientRect();
374
+ } = dragHandleRect;
347
375
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
348
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
349
- if (intersects) {
376
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
377
+ if (eventIntersects) {
378
+ // TRICKY
379
+ // We listen for pointers events at the root in order to support hit area margins
380
+ // (determining when the pointer is close enough to an element to be considered a "hit")
381
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
382
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
383
+ // and the element that was actually clicked/touched
384
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
385
+ // Calculating stacking order has a cost, so we should avoid it if possible
386
+ // That is why we only check potentially intersecting handles,
387
+ // and why we skip if the event target is within the handle's DOM
388
+ compare(targetElement, dragHandleElement) > 0) {
389
+ // If the target is above the drag handle, then we also need to confirm they overlap
390
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
391
+ //
392
+ // It's not enough to compare only the target
393
+ // The target might be a small element inside of a larger container
394
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
395
+ let currentElement = targetElement;
396
+ let didIntersect = false;
397
+ while (currentElement) {
398
+ if (currentElement.contains(dragHandleElement)) {
399
+ break;
400
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
401
+ didIntersect = true;
402
+ break;
403
+ }
404
+ currentElement = currentElement.parentElement;
405
+ }
406
+ if (didIntersect) {
407
+ return;
408
+ }
409
+ }
350
410
  intersectingHandles.push(data);
351
411
  }
352
412
  });
@@ -1795,9 +1855,6 @@ function PanelResizeHandle({
1795
1855
  const committedValuesRef = useRef({
1796
1856
  state
1797
1857
  });
1798
- useLayoutEffect(() => {
1799
- committedValuesRef.current.state = state;
1800
- });
1801
1858
  useEffect(() => {
1802
1859
  if (disabled) {
1803
1860
  setResizeHandler(null);
@@ -1910,4 +1967,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
1910
1967
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
1911
1968
  }
1912
1969
 
1913
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
1970
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
1971
+ if (!intersects(rectOne, rectTwo, strict)) {
1972
+ return {
1973
+ x: 0,
1974
+ y: 0,
1975
+ width: 0,
1976
+ height: 0
1977
+ };
1978
+ }
1979
+ return {
1980
+ x: Math.max(rectOne.x, rectTwo.x),
1981
+ y: Math.max(rectOne.y, rectTwo.y),
1982
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
1983
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
1984
+ };
1985
+ }
1986
+
1987
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, intersects };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",
@@ -66,6 +66,9 @@
66
66
  "test:watch": "jest --config=jest.config.js --watch",
67
67
  "watch": "parcel watch --port=2345"
68
68
  },
69
+ "dependencies": {
70
+ "stacking-order": "^1"
71
+ },
69
72
  "devDependencies": {
70
73
  "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
71
74
  "@babel/plugin-proposal-optional-chaining": "7.21.0",
@@ -7,7 +7,6 @@ import {
7
7
  ReactElement,
8
8
  useContext,
9
9
  useEffect,
10
- useLayoutEffect,
11
10
  useRef,
12
11
  useState,
13
12
  } from "./vendor/react";
@@ -24,6 +23,7 @@ import {
24
23
  ResizeHandlerAction,
25
24
  } from "./PanelResizeHandleRegistry";
26
25
  import { assert } from "./utils/assert";
26
+ import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
27
27
 
28
28
  export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
29
29
  export type ResizeHandlerState = "drag" | "hover" | "inactive";
@@ -97,7 +97,7 @@ export function PanelResizeHandle({
97
97
  state,
98
98
  });
99
99
 
100
- useLayoutEffect(() => {
100
+ useIsomorphicLayoutEffect(() => {
101
101
  committedValuesRef.current.state = state;
102
102
  });
103
103