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
@@ -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
 
@@ -283,6 +284,14 @@ function getInputType() {
283
284
  }
284
285
  }
285
286
 
287
+ function intersects(rectOne, rectTwo, strict) {
288
+ if (strict) {
289
+ 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;
290
+ } else {
291
+ 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;
292
+ }
293
+ }
294
+
286
295
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
287
296
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
288
297
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -321,12 +330,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
321
330
  };
322
331
  }
323
332
  function handlePointerDown(event) {
333
+ const {
334
+ target
335
+ } = event;
324
336
  const {
325
337
  x,
326
338
  y
327
339
  } = getResizeEventCoordinates(event);
328
340
  isPointerDown = true;
329
341
  recalculateIntersectingHandles({
342
+ target,
330
343
  x,
331
344
  y
332
345
  });
@@ -342,10 +355,15 @@ function handlePointerMove(event) {
342
355
  y
343
356
  } = getResizeEventCoordinates(event);
344
357
  if (!isPointerDown) {
358
+ const {
359
+ target
360
+ } = event;
361
+
345
362
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
346
363
  // at that point, the handles may not move with the pointer (depending on constraints)
347
364
  // but the same set of active handles should be locked until the pointer is released
348
365
  recalculateIntersectingHandles({
366
+ target,
349
367
  x,
350
368
  y
351
369
  });
@@ -359,6 +377,9 @@ function handlePointerMove(event) {
359
377
  }
360
378
  }
361
379
  function handlePointerUp(event) {
380
+ const {
381
+ target
382
+ } = event;
362
383
  const {
363
384
  x,
364
385
  y
@@ -368,33 +389,72 @@ function handlePointerUp(event) {
368
389
  if (intersectingHandles.length > 0) {
369
390
  event.preventDefault();
370
391
  }
392
+ updateResizeHandlerStates("up", event);
371
393
  recalculateIntersectingHandles({
394
+ target,
372
395
  x,
373
396
  y
374
397
  });
375
- updateResizeHandlerStates("up", event);
376
398
  updateCursor();
377
399
  updateListeners();
378
400
  }
379
401
  function recalculateIntersectingHandles({
402
+ target,
380
403
  x,
381
404
  y
382
405
  }) {
383
406
  intersectingHandles.splice(0);
407
+ let targetElement = null;
408
+ if (target instanceof HTMLElement) {
409
+ targetElement = target;
410
+ }
384
411
  registeredResizeHandlers.forEach(data => {
385
412
  const {
386
- element,
413
+ element: dragHandleElement,
387
414
  hitAreaMargins
388
415
  } = data;
416
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
389
417
  const {
390
418
  bottom,
391
419
  left,
392
420
  right,
393
421
  top
394
- } = element.getBoundingClientRect();
422
+ } = dragHandleRect;
395
423
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
396
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
397
- if (intersects) {
424
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
425
+ if (eventIntersects) {
426
+ // TRICKY
427
+ // We listen for pointers events at the root in order to support hit area margins
428
+ // (determining when the pointer is close enough to an element to be considered a "hit")
429
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
430
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
431
+ // and the element that was actually clicked/touched
432
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
433
+ // Calculating stacking order has a cost, so we should avoid it if possible
434
+ // That is why we only check potentially intersecting handles,
435
+ // and why we skip if the event target is within the handle's DOM
436
+ compare(targetElement, dragHandleElement) > 0) {
437
+ // If the target is above the drag handle, then we also need to confirm they overlap
438
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
439
+ //
440
+ // It's not enough to compare only the target
441
+ // The target might be a small element inside of a larger container
442
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
443
+ let currentElement = targetElement;
444
+ let didIntersect = false;
445
+ while (currentElement) {
446
+ if (currentElement.contains(dragHandleElement)) {
447
+ break;
448
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
449
+ didIntersect = true;
450
+ break;
451
+ }
452
+ currentElement = currentElement.parentElement;
453
+ }
454
+ if (didIntersect) {
455
+ return;
456
+ }
457
+ }
398
458
  intersectingHandles.push(data);
399
459
  }
400
460
  });
@@ -2242,4 +2302,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
2242
2302
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2243
2303
  }
2244
2304
 
2245
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
2305
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2306
+ if (!intersects(rectOne, rectTwo, strict)) {
2307
+ return {
2308
+ x: 0,
2309
+ y: 0,
2310
+ width: 0,
2311
+ height: 0
2312
+ };
2313
+ }
2314
+ return {
2315
+ x: Math.max(rectOne.x, rectTwo.x),
2316
+ y: Math.max(rectOne.y, rectTwo.y),
2317
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2318
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2319
+ };
2320
+ }
2321
+
2322
+ 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;
@@ -270,6 +271,14 @@ function getInputType() {
270
271
  }
271
272
  }
272
273
 
274
+ function intersects(rectOne, rectTwo, strict) {
275
+ if (strict) {
276
+ 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;
277
+ } else {
278
+ 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;
279
+ }
280
+ }
281
+
273
282
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
274
283
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
275
284
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -308,12 +317,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
308
317
  };
309
318
  }
310
319
  function handlePointerDown(event) {
320
+ const {
321
+ target
322
+ } = event;
311
323
  const {
312
324
  x,
313
325
  y
314
326
  } = getResizeEventCoordinates(event);
315
327
  isPointerDown = true;
316
328
  recalculateIntersectingHandles({
329
+ target,
317
330
  x,
318
331
  y
319
332
  });
@@ -329,10 +342,15 @@ function handlePointerMove(event) {
329
342
  y
330
343
  } = getResizeEventCoordinates(event);
331
344
  if (!isPointerDown) {
345
+ const {
346
+ target
347
+ } = event;
348
+
332
349
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
333
350
  // at that point, the handles may not move with the pointer (depending on constraints)
334
351
  // but the same set of active handles should be locked until the pointer is released
335
352
  recalculateIntersectingHandles({
353
+ target,
336
354
  x,
337
355
  y
338
356
  });
@@ -346,6 +364,9 @@ function handlePointerMove(event) {
346
364
  }
347
365
  }
348
366
  function handlePointerUp(event) {
367
+ const {
368
+ target
369
+ } = event;
349
370
  const {
350
371
  x,
351
372
  y
@@ -355,33 +376,72 @@ function handlePointerUp(event) {
355
376
  if (intersectingHandles.length > 0) {
356
377
  event.preventDefault();
357
378
  }
379
+ updateResizeHandlerStates("up", event);
358
380
  recalculateIntersectingHandles({
381
+ target,
359
382
  x,
360
383
  y
361
384
  });
362
- updateResizeHandlerStates("up", event);
363
385
  updateCursor();
364
386
  updateListeners();
365
387
  }
366
388
  function recalculateIntersectingHandles({
389
+ target,
367
390
  x,
368
391
  y
369
392
  }) {
370
393
  intersectingHandles.splice(0);
394
+ let targetElement = null;
395
+ if (target instanceof HTMLElement) {
396
+ targetElement = target;
397
+ }
371
398
  registeredResizeHandlers.forEach(data => {
372
399
  const {
373
- element,
400
+ element: dragHandleElement,
374
401
  hitAreaMargins
375
402
  } = data;
403
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
376
404
  const {
377
405
  bottom,
378
406
  left,
379
407
  right,
380
408
  top
381
- } = element.getBoundingClientRect();
409
+ } = dragHandleRect;
382
410
  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) {
411
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
412
+ if (eventIntersects) {
413
+ // TRICKY
414
+ // We listen for pointers events at the root in order to support hit area margins
415
+ // (determining when the pointer is close enough to an element to be considered a "hit")
416
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
417
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
418
+ // and the element that was actually clicked/touched
419
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
420
+ // Calculating stacking order has a cost, so we should avoid it if possible
421
+ // That is why we only check potentially intersecting handles,
422
+ // and why we skip if the event target is within the handle's DOM
423
+ stackingOrder.compare(targetElement, dragHandleElement) > 0) {
424
+ // If the target is above the drag handle, then we also need to confirm they overlap
425
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
426
+ //
427
+ // It's not enough to compare only the target
428
+ // The target might be a small element inside of a larger container
429
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
430
+ let currentElement = targetElement;
431
+ let didIntersect = false;
432
+ while (currentElement) {
433
+ if (currentElement.contains(dragHandleElement)) {
434
+ break;
435
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
436
+ didIntersect = true;
437
+ break;
438
+ }
439
+ currentElement = currentElement.parentElement;
440
+ }
441
+ if (didIntersect) {
442
+ return;
443
+ }
444
+ }
385
445
  intersectingHandles.push(data);
386
446
  }
387
447
  });
@@ -2035,10 +2095,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
2035
2095
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2036
2096
  }
2037
2097
 
2098
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2099
+ if (!intersects(rectOne, rectTwo, strict)) {
2100
+ return {
2101
+ x: 0,
2102
+ y: 0,
2103
+ width: 0,
2104
+ height: 0
2105
+ };
2106
+ }
2107
+ return {
2108
+ x: Math.max(rectOne.x, rectTwo.x),
2109
+ y: Math.max(rectOne.y, rectTwo.y),
2110
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2111
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2112
+ };
2113
+ }
2114
+
2038
2115
  exports.Panel = Panel;
2039
2116
  exports.PanelGroup = PanelGroup;
2040
2117
  exports.PanelResizeHandle = PanelResizeHandle;
2041
2118
  exports.assert = assert;
2119
+ exports.getIntersectingRectangle = getIntersectingRectangle;
2042
2120
  exports.getPanelElement = getPanelElement;
2043
2121
  exports.getPanelElementsForGroup = getPanelElementsForGroup;
2044
2122
  exports.getPanelGroupElement = getPanelGroupElement;
@@ -2046,3 +2124,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
2046
2124
  exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
2047
2125
  exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
2048
2126
  exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
2127
+ 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.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
 
@@ -246,6 +247,14 @@ function getInputType() {
246
247
  }
247
248
  }
248
249
 
250
+ function intersects(rectOne, rectTwo, strict) {
251
+ if (strict) {
252
+ 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;
253
+ } else {
254
+ 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;
255
+ }
256
+ }
257
+
249
258
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
250
259
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
251
260
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -284,12 +293,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
284
293
  };
285
294
  }
286
295
  function handlePointerDown(event) {
296
+ const {
297
+ target
298
+ } = event;
287
299
  const {
288
300
  x,
289
301
  y
290
302
  } = getResizeEventCoordinates(event);
291
303
  isPointerDown = true;
292
304
  recalculateIntersectingHandles({
305
+ target,
293
306
  x,
294
307
  y
295
308
  });
@@ -305,10 +318,15 @@ function handlePointerMove(event) {
305
318
  y
306
319
  } = getResizeEventCoordinates(event);
307
320
  if (!isPointerDown) {
321
+ const {
322
+ target
323
+ } = event;
324
+
308
325
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
309
326
  // at that point, the handles may not move with the pointer (depending on constraints)
310
327
  // but the same set of active handles should be locked until the pointer is released
311
328
  recalculateIntersectingHandles({
329
+ target,
312
330
  x,
313
331
  y
314
332
  });
@@ -322,6 +340,9 @@ function handlePointerMove(event) {
322
340
  }
323
341
  }
324
342
  function handlePointerUp(event) {
343
+ const {
344
+ target
345
+ } = event;
325
346
  const {
326
347
  x,
327
348
  y
@@ -331,33 +352,72 @@ function handlePointerUp(event) {
331
352
  if (intersectingHandles.length > 0) {
332
353
  event.preventDefault();
333
354
  }
355
+ updateResizeHandlerStates("up", event);
334
356
  recalculateIntersectingHandles({
357
+ target,
335
358
  x,
336
359
  y
337
360
  });
338
- updateResizeHandlerStates("up", event);
339
361
  updateCursor();
340
362
  updateListeners();
341
363
  }
342
364
  function recalculateIntersectingHandles({
365
+ target,
343
366
  x,
344
367
  y
345
368
  }) {
346
369
  intersectingHandles.splice(0);
370
+ let targetElement = null;
371
+ if (target instanceof HTMLElement) {
372
+ targetElement = target;
373
+ }
347
374
  registeredResizeHandlers.forEach(data => {
348
375
  const {
349
- element,
376
+ element: dragHandleElement,
350
377
  hitAreaMargins
351
378
  } = data;
379
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
352
380
  const {
353
381
  bottom,
354
382
  left,
355
383
  right,
356
384
  top
357
- } = element.getBoundingClientRect();
385
+ } = dragHandleRect;
358
386
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
359
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
360
- if (intersects) {
387
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
388
+ if (eventIntersects) {
389
+ // TRICKY
390
+ // We listen for pointers events at the root in order to support hit area margins
391
+ // (determining when the pointer is close enough to an element to be considered a "hit")
392
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
393
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
394
+ // and the element that was actually clicked/touched
395
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
396
+ // Calculating stacking order has a cost, so we should avoid it if possible
397
+ // That is why we only check potentially intersecting handles,
398
+ // and why we skip if the event target is within the handle's DOM
399
+ compare(targetElement, dragHandleElement) > 0) {
400
+ // If the target is above the drag handle, then we also need to confirm they overlap
401
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
402
+ //
403
+ // It's not enough to compare only the target
404
+ // The target might be a small element inside of a larger container
405
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
406
+ let currentElement = targetElement;
407
+ let didIntersect = false;
408
+ while (currentElement) {
409
+ if (currentElement.contains(dragHandleElement)) {
410
+ break;
411
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
412
+ didIntersect = true;
413
+ break;
414
+ }
415
+ currentElement = currentElement.parentElement;
416
+ }
417
+ if (didIntersect) {
418
+ return;
419
+ }
420
+ }
361
421
  intersectingHandles.push(data);
362
422
  }
363
423
  });
@@ -2011,4 +2071,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
2011
2071
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2012
2072
  }
2013
2073
 
2014
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
2074
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2075
+ if (!intersects(rectOne, rectTwo, strict)) {
2076
+ return {
2077
+ x: 0,
2078
+ y: 0,
2079
+ width: 0,
2080
+ height: 0
2081
+ };
2082
+ }
2083
+ return {
2084
+ x: Math.max(rectOne.x, rectTwo.x),
2085
+ y: Math.max(rectOne.y, rectTwo.y),
2086
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2087
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2088
+ };
2089
+ }
2090
+
2091
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, intersects };
@@ -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
 
@@ -272,6 +273,14 @@ function getInputType() {
272
273
  }
273
274
  }
274
275
 
276
+ function intersects(rectOne, rectTwo, strict) {
277
+ if (strict) {
278
+ 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;
279
+ } else {
280
+ 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;
281
+ }
282
+ }
283
+
275
284
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
276
285
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
277
286
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -310,12 +319,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
310
319
  };
311
320
  }
312
321
  function handlePointerDown(event) {
322
+ const {
323
+ target
324
+ } = event;
313
325
  const {
314
326
  x,
315
327
  y
316
328
  } = getResizeEventCoordinates(event);
317
329
  isPointerDown = true;
318
330
  recalculateIntersectingHandles({
331
+ target,
319
332
  x,
320
333
  y
321
334
  });
@@ -331,10 +344,15 @@ function handlePointerMove(event) {
331
344
  y
332
345
  } = getResizeEventCoordinates(event);
333
346
  if (!isPointerDown) {
347
+ const {
348
+ target
349
+ } = event;
350
+
334
351
  // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
335
352
  // at that point, the handles may not move with the pointer (depending on constraints)
336
353
  // but the same set of active handles should be locked until the pointer is released
337
354
  recalculateIntersectingHandles({
355
+ target,
338
356
  x,
339
357
  y
340
358
  });
@@ -348,6 +366,9 @@ function handlePointerMove(event) {
348
366
  }
349
367
  }
350
368
  function handlePointerUp(event) {
369
+ const {
370
+ target
371
+ } = event;
351
372
  const {
352
373
  x,
353
374
  y
@@ -357,33 +378,72 @@ function handlePointerUp(event) {
357
378
  if (intersectingHandles.length > 0) {
358
379
  event.preventDefault();
359
380
  }
381
+ updateResizeHandlerStates("up", event);
360
382
  recalculateIntersectingHandles({
383
+ target,
361
384
  x,
362
385
  y
363
386
  });
364
- updateResizeHandlerStates("up", event);
365
387
  updateCursor();
366
388
  updateListeners();
367
389
  }
368
390
  function recalculateIntersectingHandles({
391
+ target,
369
392
  x,
370
393
  y
371
394
  }) {
372
395
  intersectingHandles.splice(0);
396
+ let targetElement = null;
397
+ if (target instanceof HTMLElement) {
398
+ targetElement = target;
399
+ }
373
400
  registeredResizeHandlers.forEach(data => {
374
401
  const {
375
- element,
402
+ element: dragHandleElement,
376
403
  hitAreaMargins
377
404
  } = data;
405
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
378
406
  const {
379
407
  bottom,
380
408
  left,
381
409
  right,
382
410
  top
383
- } = element.getBoundingClientRect();
411
+ } = dragHandleRect;
384
412
  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) {
413
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
414
+ if (eventIntersects) {
415
+ // TRICKY
416
+ // We listen for pointers events at the root in order to support hit area margins
417
+ // (determining when the pointer is close enough to an element to be considered a "hit")
418
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
419
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
420
+ // and the element that was actually clicked/touched
421
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
422
+ // Calculating stacking order has a cost, so we should avoid it if possible
423
+ // That is why we only check potentially intersecting handles,
424
+ // and why we skip if the event target is within the handle's DOM
425
+ compare(targetElement, dragHandleElement) > 0) {
426
+ // If the target is above the drag handle, then we also need to confirm they overlap
427
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
428
+ //
429
+ // It's not enough to compare only the target
430
+ // The target might be a small element inside of a larger container
431
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
432
+ let currentElement = targetElement;
433
+ let didIntersect = false;
434
+ while (currentElement) {
435
+ if (currentElement.contains(dragHandleElement)) {
436
+ break;
437
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
438
+ didIntersect = true;
439
+ break;
440
+ }
441
+ currentElement = currentElement.parentElement;
442
+ }
443
+ if (didIntersect) {
444
+ return;
445
+ }
446
+ }
387
447
  intersectingHandles.push(data);
388
448
  }
389
449
  });
@@ -2131,4 +2191,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
2131
2191
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
2132
2192
  }
2133
2193
 
2134
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
2194
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
2195
+ if (!intersects(rectOne, rectTwo, strict)) {
2196
+ return {
2197
+ x: 0,
2198
+ y: 0,
2199
+ width: 0,
2200
+ height: 0
2201
+ };
2202
+ }
2203
+ return {
2204
+ x: Math.max(rectOne.x, rectTwo.x),
2205
+ y: Math.max(rectOne.y, rectTwo.y),
2206
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
2207
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
2208
+ };
2209
+ }
2210
+
2211
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, intersects };