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
|
@@ -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
|
-
} =
|
|
422
|
+
} = dragHandleRect;
|
|
395
423
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
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
|
-
|
|
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
|
-
} =
|
|
409
|
+
} = dragHandleRect;
|
|
382
410
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
383
|
-
const
|
|
384
|
-
if (
|
|
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
|
-
} =
|
|
385
|
+
} = dragHandleRect;
|
|
358
386
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
359
|
-
const
|
|
360
|
-
if (
|
|
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
|
-
|
|
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
|
-
} =
|
|
411
|
+
} = dragHandleRect;
|
|
384
412
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
385
|
-
const
|
|
386
|
-
if (
|
|
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
|
-
|
|
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 };
|