react-resizable-panels 2.0.4 → 2.0.5

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 (31) hide show
  1. package/CHANGELOG.md +4 -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/react-resizable-panels.browser.cjs.js +84 -5
  7. package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
  8. package/dist/react-resizable-panels.browser.development.cjs.js +84 -5
  9. package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
  10. package/dist/react-resizable-panels.browser.development.esm.js +83 -6
  11. package/dist/react-resizable-panels.browser.esm.js +83 -6
  12. package/dist/react-resizable-panels.cjs.js +84 -5
  13. package/dist/react-resizable-panels.cjs.mjs +3 -1
  14. package/dist/react-resizable-panels.development.cjs.js +84 -5
  15. package/dist/react-resizable-panels.development.cjs.mjs +3 -1
  16. package/dist/react-resizable-panels.development.esm.js +83 -6
  17. package/dist/react-resizable-panels.development.node.cjs.js +84 -5
  18. package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
  19. package/dist/react-resizable-panels.development.node.esm.js +83 -6
  20. package/dist/react-resizable-panels.esm.js +83 -6
  21. package/dist/react-resizable-panels.node.cjs.js +84 -5
  22. package/dist/react-resizable-panels.node.cjs.mjs +3 -1
  23. package/dist/react-resizable-panels.node.esm.js +83 -6
  24. package/package.json +4 -1
  25. package/src/PanelResizeHandleRegistry.ts +75 -8
  26. package/src/index.ts +4 -0
  27. package/src/utils/rects/getIntersectingRectangle.test.ts +198 -0
  28. package/src/utils/rects/getIntersectingRectangle.ts +28 -0
  29. package/src/utils/rects/intersects.test.ts +197 -0
  30. package/src/utils/rects/intersects.ts +23 -0
  31. package/src/utils/rects/types.ts +6 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.5
4
+
5
+ - Resize handle hit detection considers [stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context) when determining hit detection (#291)
6
+
3
7
  ## 2.0.4
4
8
 
5
9
  - Fixed `PanelResizeHandle` `onDragging` prop to only be called for the handle being dragged (#289)
@@ -9,7 +9,9 @@ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement.js";
9
9
  import { getResizeHandleElementIndex } from "./utils/dom/getResizeHandleElementIndex.js";
10
10
  import { getResizeHandleElementsForGroup } from "./utils/dom/getResizeHandleElementsForGroup.js";
11
11
  import { getResizeHandlePanelIds } from "./utils/dom/getResizeHandlePanelIds.js";
12
+ import { getIntersectingRectangle } from "./utils/rects/getIntersectingRectangle.js";
13
+ import { intersects } from "./utils/rects/intersects.js";
12
14
  import type { ImperativePanelHandle, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps } from "./Panel.js";
13
15
  import type { ImperativePanelGroupHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage } from "./PanelGroup.js";
14
16
  import type { PanelResizeHandleOnDragging, PanelResizeHandleProps } from "./PanelResizeHandle.js";
15
- export { ImperativePanelGroupHandle, ImperativePanelHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps, PanelResizeHandleOnDragging, PanelResizeHandleProps, Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, };
17
+ export { ImperativePanelGroupHandle, ImperativePanelHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps, PanelResizeHandleOnDragging, PanelResizeHandleProps, Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, intersects, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, };
@@ -0,0 +1,2 @@
1
+ import { Rectangle } from "./types.js";
2
+ export declare function getIntersectingRectangle(rectOne: Rectangle, rectTwo: Rectangle, strict: boolean): Rectangle;
@@ -0,0 +1,2 @@
1
+ import { Rectangle } from "./types.js";
2
+ export declare function intersects(rectOne: Rectangle, rectTwo: Rectangle, strict: boolean): boolean;
@@ -0,0 +1,6 @@
1
+ export interface Rectangle {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
@@ -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;
@@ -294,6 +295,14 @@ function getInputType() {
294
295
  }
295
296
  }
296
297
 
298
+ function intersects(rectOne, rectTwo, strict) {
299
+ if (strict) {
300
+ 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;
301
+ } else {
302
+ 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;
303
+ }
304
+ }
305
+
297
306
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
298
307
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
299
308
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -332,12 +341,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
332
341
  };
333
342
  }
334
343
  function handlePointerDown(event) {
344
+ const {
345
+ target
346
+ } = event;
335
347
  const {
336
348
  x,
337
349
  y
338
350
  } = getResizeEventCoordinates(event);
339
351
  isPointerDown = true;
340
352
  recalculateIntersectingHandles({
353
+ target,
341
354
  x,
342
355
  y
343
356
  });
@@ -353,10 +366,15 @@ function handlePointerMove(event) {
353
366
  y
354
367
  } = getResizeEventCoordinates(event);
355
368
  if (!isPointerDown) {
369
+ const {
370
+ target
371
+ } = event;
372
+
356
373
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
357
374
  // at that point, the handles may not move with the pointer (depending on constraints)
358
375
  // but the same set of active handles should be locked until the pointer is released
359
376
  recalculateIntersectingHandles({
377
+ target,
360
378
  x,
361
379
  y
362
380
  });
@@ -370,6 +388,9 @@ function handlePointerMove(event) {
370
388
  }
371
389
  }
372
390
  function handlePointerUp(event) {
391
+ const {
392
+ target
393
+ } = event;
373
394
  const {
374
395
  x,
375
396
  y
@@ -379,33 +400,72 @@ function handlePointerUp(event) {
379
400
  if (intersectingHandles.length > 0) {
380
401
  event.preventDefault();
381
402
  }
403
+ updateResizeHandlerStates("up", event);
382
404
  recalculateIntersectingHandles({
405
+ target,
383
406
  x,
384
407
  y
385
408
  });
386
- updateResizeHandlerStates("up", event);
387
409
  updateCursor();
388
410
  updateListeners();
389
411
  }
390
412
  function recalculateIntersectingHandles({
413
+ target,
391
414
  x,
392
415
  y
393
416
  }) {
394
417
  intersectingHandles.splice(0);
418
+ let targetElement = null;
419
+ if (target instanceof HTMLElement) {
420
+ targetElement = target;
421
+ }
395
422
  registeredResizeHandlers.forEach(data => {
396
423
  const {
397
- element,
424
+ element: dragHandleElement,
398
425
  hitAreaMargins
399
426
  } = data;
427
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
400
428
  const {
401
429
  bottom,
402
430
  left,
403
431
  right,
404
432
  top
405
- } = element.getBoundingClientRect();
433
+ } = dragHandleRect;
406
434
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
407
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
408
- if (intersects) {
435
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
436
+ if (eventIntersects) {
437
+ // TRICKY
438
+ // We listen for pointers events at the root in order to support hit area margins
439
+ // (determining when the pointer is close enough to an element to be considered a "hit")
440
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
441
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
442
+ // and the element that was actually clicked/touched
443
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
444
+ // Calculating stacking order has a cost, so we should avoid it if possible
445
+ // That is why we only check potentially intersecting handles,
446
+ // and why we skip if the event target is within the handle's DOM
447
+ stackingOrder.compare(targetElement, dragHandleElement) > 0) {
448
+ // If the target is above the drag handle, then we also need to confirm they overlap
449
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
450
+ //
451
+ // It's not enough to compare only the target
452
+ // The target might be a small element inside of a larger container
453
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
454
+ let currentElement = targetElement;
455
+ let didIntersect = false;
456
+ while (currentElement) {
457
+ if (currentElement.contains(dragHandleElement)) {
458
+ break;
459
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
460
+ didIntersect = true;
461
+ break;
462
+ }
463
+ currentElement = currentElement.parentElement;
464
+ }
465
+ if (didIntersect) {
466
+ return;
467
+ }
468
+ }
409
469
  intersectingHandles.push(data);
410
470
  }
411
471
  });
@@ -2153,10 +2213,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
2153
2213
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2154
2214
  }
2155
2215
 
2216
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2217
+ if (!intersects(rectOne, rectTwo, strict)) {
2218
+ return {
2219
+ x: 0,
2220
+ y: 0,
2221
+ width: 0,
2222
+ height: 0
2223
+ };
2224
+ }
2225
+ return {
2226
+ x: Math.max(rectOne.x, rectTwo.x),
2227
+ y: Math.max(rectOne.y, rectTwo.y),
2228
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2229
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2230
+ };
2231
+ }
2232
+
2156
2233
  exports.Panel = Panel;
2157
2234
  exports.PanelGroup = PanelGroup;
2158
2235
  exports.PanelResizeHandle = PanelResizeHandle;
2159
2236
  exports.assert = assert;
2237
+ exports.getIntersectingRectangle = getIntersectingRectangle;
2160
2238
  exports.getPanelElement = getPanelElement;
2161
2239
  exports.getPanelElementsForGroup = getPanelElementsForGroup;
2162
2240
  exports.getPanelGroupElement = getPanelGroupElement;
@@ -2164,3 +2242,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
2164
2242
  exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
2165
2243
  exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
2166
2244
  exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
2245
+ 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.browser.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;
@@ -300,6 +301,14 @@ function getInputType() {
300
301
  }
301
302
  }
302
303
 
304
+ function intersects(rectOne, rectTwo, strict) {
305
+ if (strict) {
306
+ 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;
307
+ } else {
308
+ 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;
309
+ }
310
+ }
311
+
303
312
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
304
313
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
305
314
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -338,12 +347,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
338
347
  };
339
348
  }
340
349
  function handlePointerDown(event) {
350
+ const {
351
+ target
352
+ } = event;
341
353
  const {
342
354
  x,
343
355
  y
344
356
  } = getResizeEventCoordinates(event);
345
357
  isPointerDown = true;
346
358
  recalculateIntersectingHandles({
359
+ target,
347
360
  x,
348
361
  y
349
362
  });
@@ -359,10 +372,15 @@ function handlePointerMove(event) {
359
372
  y
360
373
  } = getResizeEventCoordinates(event);
361
374
  if (!isPointerDown) {
375
+ const {
376
+ target
377
+ } = event;
378
+
362
379
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
363
380
  // at that point, the handles may not move with the pointer (depending on constraints)
364
381
  // but the same set of active handles should be locked until the pointer is released
365
382
  recalculateIntersectingHandles({
383
+ target,
366
384
  x,
367
385
  y
368
386
  });
@@ -376,6 +394,9 @@ function handlePointerMove(event) {
376
394
  }
377
395
  }
378
396
  function handlePointerUp(event) {
397
+ const {
398
+ target
399
+ } = event;
379
400
  const {
380
401
  x,
381
402
  y
@@ -385,33 +406,72 @@ function handlePointerUp(event) {
385
406
  if (intersectingHandles.length > 0) {
386
407
  event.preventDefault();
387
408
  }
409
+ updateResizeHandlerStates("up", event);
388
410
  recalculateIntersectingHandles({
411
+ target,
389
412
  x,
390
413
  y
391
414
  });
392
- updateResizeHandlerStates("up", event);
393
415
  updateCursor();
394
416
  updateListeners();
395
417
  }
396
418
  function recalculateIntersectingHandles({
419
+ target,
397
420
  x,
398
421
  y
399
422
  }) {
400
423
  intersectingHandles.splice(0);
424
+ let targetElement = null;
425
+ if (target instanceof HTMLElement) {
426
+ targetElement = target;
427
+ }
401
428
  registeredResizeHandlers.forEach(data => {
402
429
  const {
403
- element,
430
+ element: dragHandleElement,
404
431
  hitAreaMargins
405
432
  } = data;
433
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
406
434
  const {
407
435
  bottom,
408
436
  left,
409
437
  right,
410
438
  top
411
- } = element.getBoundingClientRect();
439
+ } = dragHandleRect;
412
440
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
413
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
414
- if (intersects) {
441
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
442
+ if (eventIntersects) {
443
+ // TRICKY
444
+ // We listen for pointers events at the root in order to support hit area margins
445
+ // (determining when the pointer is close enough to an element to be considered a "hit")
446
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
447
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
448
+ // and the element that was actually clicked/touched
449
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
450
+ // Calculating stacking order has a cost, so we should avoid it if possible
451
+ // That is why we only check potentially intersecting handles,
452
+ // and why we skip if the event target is within the handle's DOM
453
+ stackingOrder.compare(targetElement, dragHandleElement) > 0) {
454
+ // If the target is above the drag handle, then we also need to confirm they overlap
455
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
456
+ //
457
+ // It's not enough to compare only the target
458
+ // The target might be a small element inside of a larger container
459
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
460
+ let currentElement = targetElement;
461
+ let didIntersect = false;
462
+ while (currentElement) {
463
+ if (currentElement.contains(dragHandleElement)) {
464
+ break;
465
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
466
+ didIntersect = true;
467
+ break;
468
+ }
469
+ currentElement = currentElement.parentElement;
470
+ }
471
+ if (didIntersect) {
472
+ return;
473
+ }
474
+ }
415
475
  intersectingHandles.push(data);
416
476
  }
417
477
  });
@@ -2259,10 +2319,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
2259
2319
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2260
2320
  }
2261
2321
 
2322
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2323
+ if (!intersects(rectOne, rectTwo, strict)) {
2324
+ return {
2325
+ x: 0,
2326
+ y: 0,
2327
+ width: 0,
2328
+ height: 0
2329
+ };
2330
+ }
2331
+ return {
2332
+ x: Math.max(rectOne.x, rectTwo.x),
2333
+ y: Math.max(rectOne.y, rectTwo.y),
2334
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2335
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2336
+ };
2337
+ }
2338
+
2262
2339
  exports.Panel = Panel;
2263
2340
  exports.PanelGroup = PanelGroup;
2264
2341
  exports.PanelResizeHandle = PanelResizeHandle;
2265
2342
  exports.assert = assert;
2343
+ exports.getIntersectingRectangle = getIntersectingRectangle;
2266
2344
  exports.getPanelElement = getPanelElement;
2267
2345
  exports.getPanelElementsForGroup = getPanelElementsForGroup;
2268
2346
  exports.getPanelGroupElement = getPanelGroupElement;
@@ -2270,3 +2348,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
2270
2348
  exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
2271
2349
  exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
2272
2350
  exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
2351
+ 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.browser.development.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
 
@@ -276,6 +277,14 @@ function getInputType() {
276
277
  }
277
278
  }
278
279
 
280
+ function intersects(rectOne, rectTwo, strict) {
281
+ if (strict) {
282
+ 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;
283
+ } else {
284
+ 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;
285
+ }
286
+ }
287
+
279
288
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
280
289
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
281
290
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -314,12 +323,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
314
323
  };
315
324
  }
316
325
  function handlePointerDown(event) {
326
+ const {
327
+ target
328
+ } = event;
317
329
  const {
318
330
  x,
319
331
  y
320
332
  } = getResizeEventCoordinates(event);
321
333
  isPointerDown = true;
322
334
  recalculateIntersectingHandles({
335
+ target,
323
336
  x,
324
337
  y
325
338
  });
@@ -335,10 +348,15 @@ function handlePointerMove(event) {
335
348
  y
336
349
  } = getResizeEventCoordinates(event);
337
350
  if (!isPointerDown) {
351
+ const {
352
+ target
353
+ } = event;
354
+
338
355
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
339
356
  // at that point, the handles may not move with the pointer (depending on constraints)
340
357
  // but the same set of active handles should be locked until the pointer is released
341
358
  recalculateIntersectingHandles({
359
+ target,
342
360
  x,
343
361
  y
344
362
  });
@@ -352,6 +370,9 @@ function handlePointerMove(event) {
352
370
  }
353
371
  }
354
372
  function handlePointerUp(event) {
373
+ const {
374
+ target
375
+ } = event;
355
376
  const {
356
377
  x,
357
378
  y
@@ -361,33 +382,72 @@ function handlePointerUp(event) {
361
382
  if (intersectingHandles.length > 0) {
362
383
  event.preventDefault();
363
384
  }
385
+ updateResizeHandlerStates("up", event);
364
386
  recalculateIntersectingHandles({
387
+ target,
365
388
  x,
366
389
  y
367
390
  });
368
- updateResizeHandlerStates("up", event);
369
391
  updateCursor();
370
392
  updateListeners();
371
393
  }
372
394
  function recalculateIntersectingHandles({
395
+ target,
373
396
  x,
374
397
  y
375
398
  }) {
376
399
  intersectingHandles.splice(0);
400
+ let targetElement = null;
401
+ if (target instanceof HTMLElement) {
402
+ targetElement = target;
403
+ }
377
404
  registeredResizeHandlers.forEach(data => {
378
405
  const {
379
- element,
406
+ element: dragHandleElement,
380
407
  hitAreaMargins
381
408
  } = data;
409
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
382
410
  const {
383
411
  bottom,
384
412
  left,
385
413
  right,
386
414
  top
387
- } = element.getBoundingClientRect();
415
+ } = dragHandleRect;
388
416
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
389
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
390
- if (intersects) {
417
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
418
+ if (eventIntersects) {
419
+ // TRICKY
420
+ // We listen for pointers events at the root in order to support hit area margins
421
+ // (determining when the pointer is close enough to an element to be considered a "hit")
422
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
423
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
424
+ // and the element that was actually clicked/touched
425
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
426
+ // Calculating stacking order has a cost, so we should avoid it if possible
427
+ // That is why we only check potentially intersecting handles,
428
+ // and why we skip if the event target is within the handle's DOM
429
+ compare(targetElement, dragHandleElement) > 0) {
430
+ // If the target is above the drag handle, then we also need to confirm they overlap
431
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
432
+ //
433
+ // It's not enough to compare only the target
434
+ // The target might be a small element inside of a larger container
435
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
436
+ let currentElement = targetElement;
437
+ let didIntersect = false;
438
+ while (currentElement) {
439
+ if (currentElement.contains(dragHandleElement)) {
440
+ break;
441
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
442
+ didIntersect = true;
443
+ break;
444
+ }
445
+ currentElement = currentElement.parentElement;
446
+ }
447
+ if (didIntersect) {
448
+ return;
449
+ }
450
+ }
391
451
  intersectingHandles.push(data);
392
452
  }
393
453
  });
@@ -2235,4 +2295,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
2235
2295
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2236
2296
  }
2237
2297
 
2238
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
2298
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2299
+ if (!intersects(rectOne, rectTwo, strict)) {
2300
+ return {
2301
+ x: 0,
2302
+ y: 0,
2303
+ width: 0,
2304
+ height: 0
2305
+ };
2306
+ }
2307
+ return {
2308
+ x: Math.max(rectOne.x, rectTwo.x),
2309
+ y: Math.max(rectOne.y, rectTwo.y),
2310
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2311
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2312
+ };
2313
+ }
2314
+
2315
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, intersects };