react-resizable-panels 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -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/declarations/src/vendor/react.d.ts +3 -2
- package/dist/react-resizable-panels.browser.cjs.js +87 -7
- package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.cjs.js +87 -7
- package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.esm.js +86 -8
- package/dist/react-resizable-panels.browser.esm.js +86 -8
- package/dist/react-resizable-panels.cjs.js +87 -7
- package/dist/react-resizable-panels.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.cjs.js +87 -7
- package/dist/react-resizable-panels.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.esm.js +86 -8
- package/dist/react-resizable-panels.development.node.cjs.js +84 -8
- package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.node.esm.js +83 -9
- package/dist/react-resizable-panels.esm.js +86 -8
- package/dist/react-resizable-panels.node.cjs.js +84 -8
- package/dist/react-resizable-panels.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.node.esm.js +83 -9
- package/package.json +4 -1
- package/src/PanelResizeHandle.ts +2 -2
- package/src/PanelResizeHandleRegistry.ts +75 -8
- package/src/hooks/useIsomorphicEffect.ts +4 -2
- 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
- package/src/vendor/react.ts +3 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { compare } from 'stacking-order';
|
|
2
3
|
|
|
3
4
|
const isBrowser = typeof window !== "undefined";
|
|
4
5
|
|
|
@@ -23,11 +24,12 @@ const {
|
|
|
23
24
|
|
|
24
25
|
// `toString()` prevents bundlers from trying to `import { useId } from 'react'`
|
|
25
26
|
const useId = React["useId".toString()];
|
|
27
|
+
const useLayoutEffect_do_not_use_directly = useLayoutEffect;
|
|
26
28
|
|
|
27
29
|
const PanelGroupContext = createContext(null);
|
|
28
30
|
PanelGroupContext.displayName = "PanelGroupContext";
|
|
29
31
|
|
|
30
|
-
const useIsomorphicLayoutEffect = isBrowser ?
|
|
32
|
+
const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect_do_not_use_directly : () => {};
|
|
31
33
|
|
|
32
34
|
const wrappedUseId = typeof useId === "function" ? useId : () => null;
|
|
33
35
|
let counter = 0;
|
|
@@ -272,6 +274,14 @@ function getInputType() {
|
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
function intersects(rectOne, rectTwo, strict) {
|
|
278
|
+
if (strict) {
|
|
279
|
+
return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
|
|
280
|
+
} else {
|
|
281
|
+
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;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
275
285
|
const EXCEEDED_HORIZONTAL_MIN = 0b0001;
|
|
276
286
|
const EXCEEDED_HORIZONTAL_MAX = 0b0010;
|
|
277
287
|
const EXCEEDED_VERTICAL_MIN = 0b0100;
|
|
@@ -310,12 +320,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
|
|
|
310
320
|
};
|
|
311
321
|
}
|
|
312
322
|
function handlePointerDown(event) {
|
|
323
|
+
const {
|
|
324
|
+
target
|
|
325
|
+
} = event;
|
|
313
326
|
const {
|
|
314
327
|
x,
|
|
315
328
|
y
|
|
316
329
|
} = getResizeEventCoordinates(event);
|
|
317
330
|
isPointerDown = true;
|
|
318
331
|
recalculateIntersectingHandles({
|
|
332
|
+
target,
|
|
319
333
|
x,
|
|
320
334
|
y
|
|
321
335
|
});
|
|
@@ -331,10 +345,15 @@ function handlePointerMove(event) {
|
|
|
331
345
|
y
|
|
332
346
|
} = getResizeEventCoordinates(event);
|
|
333
347
|
if (!isPointerDown) {
|
|
348
|
+
const {
|
|
349
|
+
target
|
|
350
|
+
} = event;
|
|
351
|
+
|
|
334
352
|
// Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
|
|
335
353
|
// at that point, the handles may not move with the pointer (depending on constraints)
|
|
336
354
|
// but the same set of active handles should be locked until the pointer is released
|
|
337
355
|
recalculateIntersectingHandles({
|
|
356
|
+
target,
|
|
338
357
|
x,
|
|
339
358
|
y
|
|
340
359
|
});
|
|
@@ -348,6 +367,9 @@ function handlePointerMove(event) {
|
|
|
348
367
|
}
|
|
349
368
|
}
|
|
350
369
|
function handlePointerUp(event) {
|
|
370
|
+
const {
|
|
371
|
+
target
|
|
372
|
+
} = event;
|
|
351
373
|
const {
|
|
352
374
|
x,
|
|
353
375
|
y
|
|
@@ -357,33 +379,72 @@ function handlePointerUp(event) {
|
|
|
357
379
|
if (intersectingHandles.length > 0) {
|
|
358
380
|
event.preventDefault();
|
|
359
381
|
}
|
|
382
|
+
updateResizeHandlerStates("up", event);
|
|
360
383
|
recalculateIntersectingHandles({
|
|
384
|
+
target,
|
|
361
385
|
x,
|
|
362
386
|
y
|
|
363
387
|
});
|
|
364
|
-
updateResizeHandlerStates("up", event);
|
|
365
388
|
updateCursor();
|
|
366
389
|
updateListeners();
|
|
367
390
|
}
|
|
368
391
|
function recalculateIntersectingHandles({
|
|
392
|
+
target,
|
|
369
393
|
x,
|
|
370
394
|
y
|
|
371
395
|
}) {
|
|
372
396
|
intersectingHandles.splice(0);
|
|
397
|
+
let targetElement = null;
|
|
398
|
+
if (target instanceof HTMLElement) {
|
|
399
|
+
targetElement = target;
|
|
400
|
+
}
|
|
373
401
|
registeredResizeHandlers.forEach(data => {
|
|
374
402
|
const {
|
|
375
|
-
element,
|
|
403
|
+
element: dragHandleElement,
|
|
376
404
|
hitAreaMargins
|
|
377
405
|
} = data;
|
|
406
|
+
const dragHandleRect = dragHandleElement.getBoundingClientRect();
|
|
378
407
|
const {
|
|
379
408
|
bottom,
|
|
380
409
|
left,
|
|
381
410
|
right,
|
|
382
411
|
top
|
|
383
|
-
} =
|
|
412
|
+
} = dragHandleRect;
|
|
384
413
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
385
|
-
const
|
|
386
|
-
if (
|
|
414
|
+
const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
|
|
415
|
+
if (eventIntersects) {
|
|
416
|
+
// TRICKY
|
|
417
|
+
// We listen for pointers events at the root in order to support hit area margins
|
|
418
|
+
// (determining when the pointer is close enough to an element to be considered a "hit")
|
|
419
|
+
// Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
|
|
420
|
+
// so at this point we need to compare stacking order of a potentially intersecting drag handle,
|
|
421
|
+
// and the element that was actually clicked/touched
|
|
422
|
+
if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
|
|
423
|
+
// Calculating stacking order has a cost, so we should avoid it if possible
|
|
424
|
+
// That is why we only check potentially intersecting handles,
|
|
425
|
+
// and why we skip if the event target is within the handle's DOM
|
|
426
|
+
compare(targetElement, dragHandleElement) > 0) {
|
|
427
|
+
// If the target is above the drag handle, then we also need to confirm they overlap
|
|
428
|
+
// If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
|
|
429
|
+
//
|
|
430
|
+
// It's not enough to compare only the target
|
|
431
|
+
// The target might be a small element inside of a larger container
|
|
432
|
+
// (For example, a SPAN or a DIV inside of a larger modal dialog)
|
|
433
|
+
let currentElement = targetElement;
|
|
434
|
+
let didIntersect = false;
|
|
435
|
+
while (currentElement) {
|
|
436
|
+
if (currentElement.contains(dragHandleElement)) {
|
|
437
|
+
break;
|
|
438
|
+
} else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
|
|
439
|
+
didIntersect = true;
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
currentElement = currentElement.parentElement;
|
|
443
|
+
}
|
|
444
|
+
if (didIntersect) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
387
448
|
intersectingHandles.push(data);
|
|
388
449
|
}
|
|
389
450
|
});
|
|
@@ -2016,7 +2077,7 @@ function PanelResizeHandle({
|
|
|
2016
2077
|
const committedValuesRef = useRef({
|
|
2017
2078
|
state
|
|
2018
2079
|
});
|
|
2019
|
-
|
|
2080
|
+
useIsomorphicLayoutEffect(() => {
|
|
2020
2081
|
committedValuesRef.current.state = state;
|
|
2021
2082
|
});
|
|
2022
2083
|
useEffect(() => {
|
|
@@ -2131,4 +2192,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
|
|
|
2131
2192
|
return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
|
|
2132
2193
|
}
|
|
2133
2194
|
|
|
2134
|
-
|
|
2195
|
+
function getIntersectingRectangle(rectOne, rectTwo, strict) {
|
|
2196
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
2197
|
+
return {
|
|
2198
|
+
x: 0,
|
|
2199
|
+
y: 0,
|
|
2200
|
+
width: 0,
|
|
2201
|
+
height: 0
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
return {
|
|
2205
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
2206
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
2207
|
+
width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
|
|
2208
|
+
height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
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;
|
|
@@ -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
|
});
|
|
@@ -1819,9 +1879,6 @@ function PanelResizeHandle({
|
|
|
1819
1879
|
const committedValuesRef = useRef({
|
|
1820
1880
|
state
|
|
1821
1881
|
});
|
|
1822
|
-
useLayoutEffect(() => {
|
|
1823
|
-
committedValuesRef.current.state = state;
|
|
1824
|
-
});
|
|
1825
1882
|
useEffect(() => {
|
|
1826
1883
|
if (disabled) {
|
|
1827
1884
|
setResizeHandler(null);
|
|
@@ -1934,10 +1991,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
|
|
|
1934
1991
|
return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
|
|
1935
1992
|
}
|
|
1936
1993
|
|
|
1994
|
+
function getIntersectingRectangle(rectOne, rectTwo, strict) {
|
|
1995
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
1996
|
+
return {
|
|
1997
|
+
x: 0,
|
|
1998
|
+
y: 0,
|
|
1999
|
+
width: 0,
|
|
2000
|
+
height: 0
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
return {
|
|
2004
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
2005
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
2006
|
+
width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
|
|
2007
|
+
height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1937
2011
|
exports.Panel = Panel;
|
|
1938
2012
|
exports.PanelGroup = PanelGroup;
|
|
1939
2013
|
exports.PanelResizeHandle = PanelResizeHandle;
|
|
1940
2014
|
exports.assert = assert;
|
|
2015
|
+
exports.getIntersectingRectangle = getIntersectingRectangle;
|
|
1941
2016
|
exports.getPanelElement = getPanelElement;
|
|
1942
2017
|
exports.getPanelElementsForGroup = getPanelElementsForGroup;
|
|
1943
2018
|
exports.getPanelGroupElement = getPanelGroupElement;
|
|
@@ -1945,3 +2020,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
|
|
|
1945
2020
|
exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
|
|
1946
2021
|
exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
|
|
1947
2022
|
exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
|
|
2023
|
+
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
|
});
|
|
@@ -1795,9 +1855,6 @@ function PanelResizeHandle({
|
|
|
1795
1855
|
const committedValuesRef = useRef({
|
|
1796
1856
|
state
|
|
1797
1857
|
});
|
|
1798
|
-
useLayoutEffect(() => {
|
|
1799
|
-
committedValuesRef.current.state = state;
|
|
1800
|
-
});
|
|
1801
1858
|
useEffect(() => {
|
|
1802
1859
|
if (disabled) {
|
|
1803
1860
|
setResizeHandler(null);
|
|
@@ -1910,4 +1967,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
|
|
|
1910
1967
|
return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
|
|
1911
1968
|
}
|
|
1912
1969
|
|
|
1913
|
-
|
|
1970
|
+
function getIntersectingRectangle(rectOne, rectTwo, strict) {
|
|
1971
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
1972
|
+
return {
|
|
1973
|
+
x: 0,
|
|
1974
|
+
y: 0,
|
|
1975
|
+
width: 0,
|
|
1976
|
+
height: 0
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
return {
|
|
1980
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
1981
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
1982
|
+
width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
|
|
1983
|
+
height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
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.6",
|
|
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",
|
package/src/PanelResizeHandle.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
ReactElement,
|
|
8
8
|
useContext,
|
|
9
9
|
useEffect,
|
|
10
|
-
useLayoutEffect,
|
|
11
10
|
useRef,
|
|
12
11
|
useState,
|
|
13
12
|
} from "./vendor/react";
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
ResizeHandlerAction,
|
|
25
24
|
} from "./PanelResizeHandleRegistry";
|
|
26
25
|
import { assert } from "./utils/assert";
|
|
26
|
+
import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
|
|
27
27
|
|
|
28
28
|
export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
|
|
29
29
|
export type ResizeHandlerState = "drag" | "hover" | "inactive";
|
|
@@ -97,7 +97,7 @@ export function PanelResizeHandle({
|
|
|
97
97
|
state,
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
useIsomorphicLayoutEffect(() => {
|
|
101
101
|
committedValuesRef.current.state = state;
|
|
102
102
|
});
|
|
103
103
|
|