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
  // This module exists to work around Webpack issue https://github.com/webpack/webpack/issues/14814
4
5
 
@@ -21,11 +22,12 @@ const {
21
22
 
22
23
  // `toString()` prevents bundlers from trying to `import { useId } from 'react'`
23
24
  const useId = React["useId".toString()];
25
+ const useLayoutEffect_do_not_use_directly = useLayoutEffect;
24
26
 
25
27
  const PanelGroupContext = createContext(null);
26
28
  PanelGroupContext.displayName = "PanelGroupContext";
27
29
 
28
- const useIsomorphicLayoutEffect = useLayoutEffect ;
30
+ const useIsomorphicLayoutEffect = useLayoutEffect_do_not_use_directly ;
29
31
 
30
32
  const wrappedUseId = typeof useId === "function" ? useId : () => null;
31
33
  let counter = 0;
@@ -270,6 +272,14 @@ function getInputType() {
270
272
  }
271
273
  }
272
274
 
275
+ function intersects(rectOne, rectTwo, strict) {
276
+ if (strict) {
277
+ 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;
278
+ } else {
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
+ }
281
+ }
282
+
273
283
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
274
284
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
275
285
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -308,12 +318,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
308
318
  };
309
319
  }
310
320
  function handlePointerDown(event) {
321
+ const {
322
+ target
323
+ } = event;
311
324
  const {
312
325
  x,
313
326
  y
314
327
  } = getResizeEventCoordinates(event);
315
328
  isPointerDown = true;
316
329
  recalculateIntersectingHandles({
330
+ target,
317
331
  x,
318
332
  y
319
333
  });
@@ -329,10 +343,15 @@ function handlePointerMove(event) {
329
343
  y
330
344
  } = getResizeEventCoordinates(event);
331
345
  if (!isPointerDown) {
346
+ const {
347
+ target
348
+ } = event;
349
+
332
350
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
333
351
  // at that point, the handles may not move with the pointer (depending on constraints)
334
352
  // but the same set of active handles should be locked until the pointer is released
335
353
  recalculateIntersectingHandles({
354
+ target,
336
355
  x,
337
356
  y
338
357
  });
@@ -346,6 +365,9 @@ function handlePointerMove(event) {
346
365
  }
347
366
  }
348
367
  function handlePointerUp(event) {
368
+ const {
369
+ target
370
+ } = event;
349
371
  const {
350
372
  x,
351
373
  y
@@ -355,33 +377,72 @@ function handlePointerUp(event) {
355
377
  if (intersectingHandles.length > 0) {
356
378
  event.preventDefault();
357
379
  }
380
+ updateResizeHandlerStates("up", event);
358
381
  recalculateIntersectingHandles({
382
+ target,
359
383
  x,
360
384
  y
361
385
  });
362
- updateResizeHandlerStates("up", event);
363
386
  updateCursor();
364
387
  updateListeners();
365
388
  }
366
389
  function recalculateIntersectingHandles({
390
+ target,
367
391
  x,
368
392
  y
369
393
  }) {
370
394
  intersectingHandles.splice(0);
395
+ let targetElement = null;
396
+ if (target instanceof HTMLElement) {
397
+ targetElement = target;
398
+ }
371
399
  registeredResizeHandlers.forEach(data => {
372
400
  const {
373
- element,
401
+ element: dragHandleElement,
374
402
  hitAreaMargins
375
403
  } = data;
404
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
376
405
  const {
377
406
  bottom,
378
407
  left,
379
408
  right,
380
409
  top
381
- } = element.getBoundingClientRect();
410
+ } = dragHandleRect;
382
411
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
383
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
384
- if (intersects) {
412
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
413
+ if (eventIntersects) {
414
+ // TRICKY
415
+ // We listen for pointers events at the root in order to support hit area margins
416
+ // (determining when the pointer is close enough to an element to be considered a "hit")
417
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
418
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
419
+ // and the element that was actually clicked/touched
420
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
421
+ // Calculating stacking order has a cost, so we should avoid it if possible
422
+ // That is why we only check potentially intersecting handles,
423
+ // and why we skip if the event target is within the handle's DOM
424
+ compare(targetElement, dragHandleElement) > 0) {
425
+ // If the target is above the drag handle, then we also need to confirm they overlap
426
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
427
+ //
428
+ // It's not enough to compare only the target
429
+ // The target might be a small element inside of a larger container
430
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
431
+ let currentElement = targetElement;
432
+ let didIntersect = false;
433
+ while (currentElement) {
434
+ if (currentElement.contains(dragHandleElement)) {
435
+ break;
436
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
437
+ didIntersect = true;
438
+ break;
439
+ }
440
+ currentElement = currentElement.parentElement;
441
+ }
442
+ if (didIntersect) {
443
+ return;
444
+ }
445
+ }
385
446
  intersectingHandles.push(data);
386
447
  }
387
448
  });
@@ -2014,7 +2075,7 @@ function PanelResizeHandle({
2014
2075
  const committedValuesRef = useRef({
2015
2076
  state
2016
2077
  });
2017
- useLayoutEffect(() => {
2078
+ useIsomorphicLayoutEffect(() => {
2018
2079
  committedValuesRef.current.state = state;
2019
2080
  });
2020
2081
  useEffect(() => {
@@ -2129,4 +2190,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
2129
2190
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2130
2191
  }
2131
2192
 
2132
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
2193
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2194
+ if (!intersects(rectOne, rectTwo, strict)) {
2195
+ return {
2196
+ x: 0,
2197
+ y: 0,
2198
+ width: 0,
2199
+ height: 0
2200
+ };
2201
+ }
2202
+ return {
2203
+ x: Math.max(rectOne.x, rectTwo.x),
2204
+ y: Math.max(rectOne.y, rectTwo.y),
2205
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2206
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2207
+ };
2208
+ }
2209
+
2210
+ 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;
@@ -47,11 +48,12 @@ const {
47
48
 
48
49
  // `toString()` prevents bundlers from trying to `import { useId } from 'react'`
49
50
  const useId = React__namespace["useId".toString()];
51
+ const useLayoutEffect_do_not_use_directly = useLayoutEffect;
50
52
 
51
53
  const PanelGroupContext = createContext(null);
52
54
  PanelGroupContext.displayName = "PanelGroupContext";
53
55
 
54
- const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : () => {};
56
+ const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect_do_not_use_directly : () => {};
55
57
 
56
58
  const wrappedUseId = typeof useId === "function" ? useId : () => null;
57
59
  let counter = 0;
@@ -296,6 +298,14 @@ function getInputType() {
296
298
  }
297
299
  }
298
300
 
301
+ function intersects(rectOne, rectTwo, strict) {
302
+ if (strict) {
303
+ 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;
304
+ } else {
305
+ 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;
306
+ }
307
+ }
308
+
299
309
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
300
310
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
301
311
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -334,12 +344,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
334
344
  };
335
345
  }
336
346
  function handlePointerDown(event) {
347
+ const {
348
+ target
349
+ } = event;
337
350
  const {
338
351
  x,
339
352
  y
340
353
  } = getResizeEventCoordinates(event);
341
354
  isPointerDown = true;
342
355
  recalculateIntersectingHandles({
356
+ target,
343
357
  x,
344
358
  y
345
359
  });
@@ -355,10 +369,15 @@ function handlePointerMove(event) {
355
369
  y
356
370
  } = getResizeEventCoordinates(event);
357
371
  if (!isPointerDown) {
372
+ const {
373
+ target
374
+ } = event;
375
+
358
376
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
359
377
  // at that point, the handles may not move with the pointer (depending on constraints)
360
378
  // but the same set of active handles should be locked until the pointer is released
361
379
  recalculateIntersectingHandles({
380
+ target,
362
381
  x,
363
382
  y
364
383
  });
@@ -372,6 +391,9 @@ function handlePointerMove(event) {
372
391
  }
373
392
  }
374
393
  function handlePointerUp(event) {
394
+ const {
395
+ target
396
+ } = event;
375
397
  const {
376
398
  x,
377
399
  y
@@ -381,33 +403,72 @@ function handlePointerUp(event) {
381
403
  if (intersectingHandles.length > 0) {
382
404
  event.preventDefault();
383
405
  }
406
+ updateResizeHandlerStates("up", event);
384
407
  recalculateIntersectingHandles({
408
+ target,
385
409
  x,
386
410
  y
387
411
  });
388
- updateResizeHandlerStates("up", event);
389
412
  updateCursor();
390
413
  updateListeners();
391
414
  }
392
415
  function recalculateIntersectingHandles({
416
+ target,
393
417
  x,
394
418
  y
395
419
  }) {
396
420
  intersectingHandles.splice(0);
421
+ let targetElement = null;
422
+ if (target instanceof HTMLElement) {
423
+ targetElement = target;
424
+ }
397
425
  registeredResizeHandlers.forEach(data => {
398
426
  const {
399
- element,
427
+ element: dragHandleElement,
400
428
  hitAreaMargins
401
429
  } = data;
430
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
402
431
  const {
403
432
  bottom,
404
433
  left,
405
434
  right,
406
435
  top
407
- } = element.getBoundingClientRect();
436
+ } = dragHandleRect;
408
437
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
409
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
410
- if (intersects) {
438
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
439
+ if (eventIntersects) {
440
+ // TRICKY
441
+ // We listen for pointers events at the root in order to support hit area margins
442
+ // (determining when the pointer is close enough to an element to be considered a "hit")
443
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
444
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
445
+ // and the element that was actually clicked/touched
446
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
447
+ // Calculating stacking order has a cost, so we should avoid it if possible
448
+ // That is why we only check potentially intersecting handles,
449
+ // and why we skip if the event target is within the handle's DOM
450
+ stackingOrder.compare(targetElement, dragHandleElement) > 0) {
451
+ // If the target is above the drag handle, then we also need to confirm they overlap
452
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
453
+ //
454
+ // It's not enough to compare only the target
455
+ // The target might be a small element inside of a larger container
456
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
457
+ let currentElement = targetElement;
458
+ let didIntersect = false;
459
+ while (currentElement) {
460
+ if (currentElement.contains(dragHandleElement)) {
461
+ break;
462
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
463
+ didIntersect = true;
464
+ break;
465
+ }
466
+ currentElement = currentElement.parentElement;
467
+ }
468
+ if (didIntersect) {
469
+ return;
470
+ }
471
+ }
411
472
  intersectingHandles.push(data);
412
473
  }
413
474
  });
@@ -2040,7 +2101,7 @@ function PanelResizeHandle({
2040
2101
  const committedValuesRef = useRef({
2041
2102
  state
2042
2103
  });
2043
- useLayoutEffect(() => {
2104
+ useIsomorphicLayoutEffect(() => {
2044
2105
  committedValuesRef.current.state = state;
2045
2106
  });
2046
2107
  useEffect(() => {
@@ -2155,10 +2216,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
2155
2216
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2156
2217
  }
2157
2218
 
2219
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2220
+ if (!intersects(rectOne, rectTwo, strict)) {
2221
+ return {
2222
+ x: 0,
2223
+ y: 0,
2224
+ width: 0,
2225
+ height: 0
2226
+ };
2227
+ }
2228
+ return {
2229
+ x: Math.max(rectOne.x, rectTwo.x),
2230
+ y: Math.max(rectOne.y, rectTwo.y),
2231
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2232
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2233
+ };
2234
+ }
2235
+
2158
2236
  exports.Panel = Panel;
2159
2237
  exports.PanelGroup = PanelGroup;
2160
2238
  exports.PanelResizeHandle = PanelResizeHandle;
2161
2239
  exports.assert = assert;
2240
+ exports.getIntersectingRectangle = getIntersectingRectangle;
2162
2241
  exports.getPanelElement = getPanelElement;
2163
2242
  exports.getPanelElementsForGroup = getPanelElementsForGroup;
2164
2243
  exports.getPanelGroupElement = getPanelGroupElement;
@@ -2166,3 +2245,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
2166
2245
  exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
2167
2246
  exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
2168
2247
  exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
2248
+ 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.cjs.js";
@@ -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;
@@ -47,11 +48,12 @@ const {
47
48
 
48
49
  // `toString()` prevents bundlers from trying to `import { useId } from 'react'`
49
50
  const useId = React__namespace["useId".toString()];
51
+ const useLayoutEffect_do_not_use_directly = useLayoutEffect;
50
52
 
51
53
  const PanelGroupContext = createContext(null);
52
54
  PanelGroupContext.displayName = "PanelGroupContext";
53
55
 
54
- const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : () => {};
56
+ const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect_do_not_use_directly : () => {};
55
57
 
56
58
  const wrappedUseId = typeof useId === "function" ? useId : () => null;
57
59
  let counter = 0;
@@ -307,6 +309,14 @@ function getInputType() {
307
309
  }
308
310
  }
309
311
 
312
+ function intersects(rectOne, rectTwo, strict) {
313
+ if (strict) {
314
+ 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;
315
+ } else {
316
+ 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;
317
+ }
318
+ }
319
+
310
320
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
311
321
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
312
322
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -345,12 +355,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
345
355
  };
346
356
  }
347
357
  function handlePointerDown(event) {
358
+ const {
359
+ target
360
+ } = event;
348
361
  const {
349
362
  x,
350
363
  y
351
364
  } = getResizeEventCoordinates(event);
352
365
  isPointerDown = true;
353
366
  recalculateIntersectingHandles({
367
+ target,
354
368
  x,
355
369
  y
356
370
  });
@@ -366,10 +380,15 @@ function handlePointerMove(event) {
366
380
  y
367
381
  } = getResizeEventCoordinates(event);
368
382
  if (!isPointerDown) {
383
+ const {
384
+ target
385
+ } = event;
386
+
369
387
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
370
388
  // at that point, the handles may not move with the pointer (depending on constraints)
371
389
  // but the same set of active handles should be locked until the pointer is released
372
390
  recalculateIntersectingHandles({
391
+ target,
373
392
  x,
374
393
  y
375
394
  });
@@ -383,6 +402,9 @@ function handlePointerMove(event) {
383
402
  }
384
403
  }
385
404
  function handlePointerUp(event) {
405
+ const {
406
+ target
407
+ } = event;
386
408
  const {
387
409
  x,
388
410
  y
@@ -392,33 +414,72 @@ function handlePointerUp(event) {
392
414
  if (intersectingHandles.length > 0) {
393
415
  event.preventDefault();
394
416
  }
417
+ updateResizeHandlerStates("up", event);
395
418
  recalculateIntersectingHandles({
419
+ target,
396
420
  x,
397
421
  y
398
422
  });
399
- updateResizeHandlerStates("up", event);
400
423
  updateCursor();
401
424
  updateListeners();
402
425
  }
403
426
  function recalculateIntersectingHandles({
427
+ target,
404
428
  x,
405
429
  y
406
430
  }) {
407
431
  intersectingHandles.splice(0);
432
+ let targetElement = null;
433
+ if (target instanceof HTMLElement) {
434
+ targetElement = target;
435
+ }
408
436
  registeredResizeHandlers.forEach(data => {
409
437
  const {
410
- element,
438
+ element: dragHandleElement,
411
439
  hitAreaMargins
412
440
  } = data;
441
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
413
442
  const {
414
443
  bottom,
415
444
  left,
416
445
  right,
417
446
  top
418
- } = element.getBoundingClientRect();
447
+ } = dragHandleRect;
419
448
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
420
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
421
- if (intersects) {
449
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
450
+ if (eventIntersects) {
451
+ // TRICKY
452
+ // We listen for pointers events at the root in order to support hit area margins
453
+ // (determining when the pointer is close enough to an element to be considered a "hit")
454
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
455
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
456
+ // and the element that was actually clicked/touched
457
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
458
+ // Calculating stacking order has a cost, so we should avoid it if possible
459
+ // That is why we only check potentially intersecting handles,
460
+ // and why we skip if the event target is within the handle's DOM
461
+ stackingOrder.compare(targetElement, dragHandleElement) > 0) {
462
+ // If the target is above the drag handle, then we also need to confirm they overlap
463
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
464
+ //
465
+ // It's not enough to compare only the target
466
+ // The target might be a small element inside of a larger container
467
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
468
+ let currentElement = targetElement;
469
+ let didIntersect = false;
470
+ while (currentElement) {
471
+ if (currentElement.contains(dragHandleElement)) {
472
+ break;
473
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
474
+ didIntersect = true;
475
+ break;
476
+ }
477
+ currentElement = currentElement.parentElement;
478
+ }
479
+ if (didIntersect) {
480
+ return;
481
+ }
482
+ }
422
483
  intersectingHandles.push(data);
423
484
  }
424
485
  });
@@ -2151,7 +2212,7 @@ function PanelResizeHandle({
2151
2212
  const committedValuesRef = useRef({
2152
2213
  state
2153
2214
  });
2154
- useLayoutEffect(() => {
2215
+ useIsomorphicLayoutEffect(() => {
2155
2216
  committedValuesRef.current.state = state;
2156
2217
  });
2157
2218
  useEffect(() => {
@@ -2266,10 +2327,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
2266
2327
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2267
2328
  }
2268
2329
 
2330
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2331
+ if (!intersects(rectOne, rectTwo, strict)) {
2332
+ return {
2333
+ x: 0,
2334
+ y: 0,
2335
+ width: 0,
2336
+ height: 0
2337
+ };
2338
+ }
2339
+ return {
2340
+ x: Math.max(rectOne.x, rectTwo.x),
2341
+ y: Math.max(rectOne.y, rectTwo.y),
2342
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2343
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2344
+ };
2345
+ }
2346
+
2269
2347
  exports.Panel = Panel;
2270
2348
  exports.PanelGroup = PanelGroup;
2271
2349
  exports.PanelResizeHandle = PanelResizeHandle;
2272
2350
  exports.assert = assert;
2351
+ exports.getIntersectingRectangle = getIntersectingRectangle;
2273
2352
  exports.getPanelElement = getPanelElement;
2274
2353
  exports.getPanelElementsForGroup = getPanelElementsForGroup;
2275
2354
  exports.getPanelGroupElement = getPanelGroupElement;
@@ -2277,3 +2356,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
2277
2356
  exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
2278
2357
  exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
2279
2358
  exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
2359
+ 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.development.cjs.js";