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.
- package/CHANGELOG.md +4 -0
- package/dist/declarations/src/index.d.ts +3 -1
- package/dist/declarations/src/utils/rects/getIntersectingRectangle.d.ts +2 -0
- package/dist/declarations/src/utils/rects/intersects.d.ts +2 -0
- package/dist/declarations/src/utils/rects/types.d.ts +6 -0
- package/dist/react-resizable-panels.browser.cjs.js +84 -5
- package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.cjs.js +84 -5
- package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.esm.js +83 -6
- package/dist/react-resizable-panels.browser.esm.js +83 -6
- package/dist/react-resizable-panels.cjs.js +84 -5
- package/dist/react-resizable-panels.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.cjs.js +84 -5
- package/dist/react-resizable-panels.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.esm.js +83 -6
- package/dist/react-resizable-panels.development.node.cjs.js +84 -5
- package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.node.esm.js +83 -6
- package/dist/react-resizable-panels.esm.js +83 -6
- package/dist/react-resizable-panels.node.cjs.js +84 -5
- package/dist/react-resizable-panels.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.node.esm.js +83 -6
- package/package.json +4 -1
- package/src/PanelResizeHandleRegistry.ts +75 -8
- package/src/index.ts +4 -0
- package/src/utils/rects/getIntersectingRectangle.test.ts +198 -0
- package/src/utils/rects/getIntersectingRectangle.ts +28 -0
- package/src/utils/rects/intersects.test.ts +197 -0
- package/src/utils/rects/intersects.ts +23 -0
- package/src/utils/rects/types.ts +6 -0
|
@@ -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
|
-
} =
|
|
398
|
+
} = dragHandleRect;
|
|
371
399
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
372
|
-
const
|
|
373
|
-
if (
|
|
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
|
});
|
|
@@ -1934,10 +1994,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
|
|
|
1934
1994
|
return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
|
|
1935
1995
|
}
|
|
1936
1996
|
|
|
1997
|
+
function getIntersectingRectangle(rectOne, rectTwo, strict) {
|
|
1998
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
1999
|
+
return {
|
|
2000
|
+
x: 0,
|
|
2001
|
+
y: 0,
|
|
2002
|
+
width: 0,
|
|
2003
|
+
height: 0
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
return {
|
|
2007
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
2008
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
2009
|
+
width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
|
|
2010
|
+
height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1937
2014
|
exports.Panel = Panel;
|
|
1938
2015
|
exports.PanelGroup = PanelGroup;
|
|
1939
2016
|
exports.PanelResizeHandle = PanelResizeHandle;
|
|
1940
2017
|
exports.assert = assert;
|
|
2018
|
+
exports.getIntersectingRectangle = getIntersectingRectangle;
|
|
1941
2019
|
exports.getPanelElement = getPanelElement;
|
|
1942
2020
|
exports.getPanelElementsForGroup = getPanelElementsForGroup;
|
|
1943
2021
|
exports.getPanelGroupElement = getPanelGroupElement;
|
|
@@ -1945,3 +2023,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
|
|
|
1945
2023
|
exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
|
|
1946
2024
|
exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
|
|
1947
2025
|
exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
|
|
2026
|
+
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
|
-
} =
|
|
374
|
+
} = dragHandleRect;
|
|
347
375
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
348
|
-
const
|
|
349
|
-
if (
|
|
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
|
});
|
|
@@ -1910,4 +1970,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
|
|
|
1910
1970
|
return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
|
|
1911
1971
|
}
|
|
1912
1972
|
|
|
1913
|
-
|
|
1973
|
+
function getIntersectingRectangle(rectOne, rectTwo, strict) {
|
|
1974
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
1975
|
+
return {
|
|
1976
|
+
x: 0,
|
|
1977
|
+
y: 0,
|
|
1978
|
+
width: 0,
|
|
1979
|
+
height: 0
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
return {
|
|
1983
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
1984
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
1985
|
+
width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
|
|
1986
|
+
height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
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.
|
|
3
|
+
"version": "2.0.5",
|
|
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",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { compare } from "stacking-order";
|
|
1
2
|
import { Direction, ResizeEvent } from "./types";
|
|
2
3
|
import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
3
4
|
import { getResizeEventCoordinates } from "./utils/events/getResizeEventCoordinates";
|
|
4
5
|
import { getInputType } from "./utils/getInputType";
|
|
6
|
+
import { intersects } from "./utils/rects/intersects";
|
|
5
7
|
|
|
6
8
|
export type ResizeHandlerAction = "down" | "move" | "up";
|
|
7
9
|
export type SetResizeHandlerState = (
|
|
@@ -75,11 +77,12 @@ export function registerResizeHandle(
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
function handlePointerDown(event: ResizeEvent) {
|
|
80
|
+
const { target } = event;
|
|
78
81
|
const { x, y } = getResizeEventCoordinates(event);
|
|
79
82
|
|
|
80
83
|
isPointerDown = true;
|
|
81
84
|
|
|
82
|
-
recalculateIntersectingHandles({ x, y });
|
|
85
|
+
recalculateIntersectingHandles({ target, x, y });
|
|
83
86
|
updateListeners();
|
|
84
87
|
|
|
85
88
|
if (intersectingHandles.length > 0) {
|
|
@@ -93,10 +96,12 @@ function handlePointerMove(event: ResizeEvent) {
|
|
|
93
96
|
const { x, y } = getResizeEventCoordinates(event);
|
|
94
97
|
|
|
95
98
|
if (!isPointerDown) {
|
|
99
|
+
const { target } = event;
|
|
100
|
+
|
|
96
101
|
// Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
|
|
97
102
|
// at that point, the handles may not move with the pointer (depending on constraints)
|
|
98
103
|
// but the same set of active handles should be locked until the pointer is released
|
|
99
|
-
recalculateIntersectingHandles({ x, y });
|
|
104
|
+
recalculateIntersectingHandles({ target, x, y });
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
updateResizeHandlerStates("move", event);
|
|
@@ -110,6 +115,7 @@ function handlePointerMove(event: ResizeEvent) {
|
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
function handlePointerUp(event: ResizeEvent) {
|
|
118
|
+
const { target } = event;
|
|
113
119
|
const { x, y } = getResizeEventCoordinates(event);
|
|
114
120
|
|
|
115
121
|
panelConstraintFlags.clear();
|
|
@@ -119,31 +125,92 @@ function handlePointerUp(event: ResizeEvent) {
|
|
|
119
125
|
event.preventDefault();
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
recalculateIntersectingHandles({ x, y });
|
|
123
128
|
updateResizeHandlerStates("up", event);
|
|
129
|
+
recalculateIntersectingHandles({ target, x, y });
|
|
124
130
|
updateCursor();
|
|
125
131
|
|
|
126
132
|
updateListeners();
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
function recalculateIntersectingHandles({
|
|
135
|
+
function recalculateIntersectingHandles({
|
|
136
|
+
target,
|
|
137
|
+
x,
|
|
138
|
+
y,
|
|
139
|
+
}: {
|
|
140
|
+
target: EventTarget | null;
|
|
141
|
+
x: number;
|
|
142
|
+
y: number;
|
|
143
|
+
}) {
|
|
130
144
|
intersectingHandles.splice(0);
|
|
131
145
|
|
|
146
|
+
let targetElement: HTMLElement | null = null;
|
|
147
|
+
if (target instanceof HTMLElement) {
|
|
148
|
+
targetElement = target;
|
|
149
|
+
}
|
|
150
|
+
|
|
132
151
|
registeredResizeHandlers.forEach((data) => {
|
|
133
|
-
const { element, hitAreaMargins } = data;
|
|
134
|
-
|
|
152
|
+
const { element: dragHandleElement, hitAreaMargins } = data;
|
|
153
|
+
|
|
154
|
+
const dragHandleRect = dragHandleElement.getBoundingClientRect();
|
|
155
|
+
const { bottom, left, right, top } = dragHandleRect;
|
|
135
156
|
|
|
136
157
|
const margin = isCoarsePointer
|
|
137
158
|
? hitAreaMargins.coarse
|
|
138
159
|
: hitAreaMargins.fine;
|
|
139
160
|
|
|
140
|
-
const
|
|
161
|
+
const eventIntersects =
|
|
141
162
|
x >= left - margin &&
|
|
142
163
|
x <= right + margin &&
|
|
143
164
|
y >= top - margin &&
|
|
144
165
|
y <= bottom + margin;
|
|
145
166
|
|
|
146
|
-
if (
|
|
167
|
+
if (eventIntersects) {
|
|
168
|
+
// TRICKY
|
|
169
|
+
// We listen for pointers events at the root in order to support hit area margins
|
|
170
|
+
// (determining when the pointer is close enough to an element to be considered a "hit")
|
|
171
|
+
// Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
|
|
172
|
+
// so at this point we need to compare stacking order of a potentially intersecting drag handle,
|
|
173
|
+
// and the element that was actually clicked/touched
|
|
174
|
+
if (
|
|
175
|
+
targetElement !== null &&
|
|
176
|
+
dragHandleElement !== targetElement &&
|
|
177
|
+
!dragHandleElement.contains(targetElement) &&
|
|
178
|
+
!targetElement.contains(dragHandleElement) &&
|
|
179
|
+
// Calculating stacking order has a cost, so we should avoid it if possible
|
|
180
|
+
// That is why we only check potentially intersecting handles,
|
|
181
|
+
// and why we skip if the event target is within the handle's DOM
|
|
182
|
+
compare(targetElement, dragHandleElement) > 0
|
|
183
|
+
) {
|
|
184
|
+
// If the target is above the drag handle, then we also need to confirm they overlap
|
|
185
|
+
// If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
|
|
186
|
+
//
|
|
187
|
+
// It's not enough to compare only the target
|
|
188
|
+
// The target might be a small element inside of a larger container
|
|
189
|
+
// (For example, a SPAN or a DIV inside of a larger modal dialog)
|
|
190
|
+
let currentElement: HTMLElement | null = targetElement;
|
|
191
|
+
let didIntersect = false;
|
|
192
|
+
while (currentElement) {
|
|
193
|
+
if (currentElement.contains(dragHandleElement)) {
|
|
194
|
+
break;
|
|
195
|
+
} else if (
|
|
196
|
+
intersects(
|
|
197
|
+
currentElement.getBoundingClientRect(),
|
|
198
|
+
dragHandleRect,
|
|
199
|
+
true
|
|
200
|
+
)
|
|
201
|
+
) {
|
|
202
|
+
didIntersect = true;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
currentElement = currentElement.parentElement;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (didIntersect) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
147
214
|
intersectingHandles.push(data);
|
|
148
215
|
}
|
|
149
216
|
});
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
|
|
|
9
9
|
import { getResizeHandleElementIndex } from "./utils/dom/getResizeHandleElementIndex";
|
|
10
10
|
import { getResizeHandleElementsForGroup } from "./utils/dom/getResizeHandleElementsForGroup";
|
|
11
11
|
import { getResizeHandlePanelIds } from "./utils/dom/getResizeHandlePanelIds";
|
|
12
|
+
import { getIntersectingRectangle } from "./utils/rects/getIntersectingRectangle";
|
|
13
|
+
import { intersects } from "./utils/rects/intersects";
|
|
12
14
|
|
|
13
15
|
import type {
|
|
14
16
|
ImperativePanelHandle,
|
|
@@ -49,6 +51,8 @@ export {
|
|
|
49
51
|
|
|
50
52
|
// Utility methods
|
|
51
53
|
assert,
|
|
54
|
+
getIntersectingRectangle,
|
|
55
|
+
intersects,
|
|
52
56
|
|
|
53
57
|
// DOM helpers
|
|
54
58
|
getPanelElement,
|