react-resizable-panels 2.0.3 → 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 +8 -0
- package/dist/declarations/src/PanelResizeHandle.d.ts +1 -0
- package/dist/declarations/src/PanelResizeHandleRegistry.d.ts +1 -2
- 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 +138 -56
- package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.cjs.js +138 -56
- package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.esm.js +137 -57
- package/dist/react-resizable-panels.browser.esm.js +137 -57
- package/dist/react-resizable-panels.cjs.js +138 -56
- package/dist/react-resizable-panels.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.cjs.js +138 -56
- package/dist/react-resizable-panels.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.esm.js +137 -57
- package/dist/react-resizable-panels.development.node.cjs.js +138 -56
- package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.node.esm.js +137 -57
- package/dist/react-resizable-panels.esm.js +137 -57
- package/dist/react-resizable-panels.node.cjs.js +138 -56
- package/dist/react-resizable-panels.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.node.esm.js +137 -57
- package/package.json +4 -1
- package/src/Panel.test.tsx +63 -0
- package/src/PanelGroup.test.tsx +21 -1
- package/src/PanelResizeHandle.test.tsx +181 -22
- package/src/PanelResizeHandle.ts +44 -24
- package/src/PanelResizeHandleRegistry.ts +87 -30
- 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/utils/test-utils.ts +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.5
|
|
4
|
+
|
|
5
|
+
- Resize handle hit detection considers [stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context) when determining hit detection (#291)
|
|
6
|
+
|
|
7
|
+
## 2.0.4
|
|
8
|
+
|
|
9
|
+
- Fixed `PanelResizeHandle` `onDragging` prop to only be called for the handle being dragged (#289)
|
|
10
|
+
|
|
3
11
|
## 2.0.3
|
|
4
12
|
|
|
5
13
|
- Fix resize handle onDragging callback (#278)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CSSProperties, HTMLAttributes, PropsWithChildren, ReactElement } from "./vendor/react.js";
|
|
2
2
|
import { PointerHitAreaMargins } from "./PanelResizeHandleRegistry.js";
|
|
3
3
|
export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
|
|
4
|
+
export type ResizeHandlerState = "drag" | "hover" | "inactive";
|
|
4
5
|
export type PanelResizeHandleProps = Omit<HTMLAttributes<keyof HTMLElementTagNameMap>, "id"> & PropsWithChildren<{
|
|
5
6
|
className?: string;
|
|
6
7
|
disabled?: boolean;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Direction, ResizeEvent } from "./types.js";
|
|
2
2
|
export type ResizeHandlerAction = "down" | "move" | "up";
|
|
3
|
-
export type
|
|
4
|
-
export type SetResizeHandlerState = (action: ResizeHandlerAction, state: ResizeHandlerState, event: ResizeEvent) => void;
|
|
3
|
+
export type SetResizeHandlerState = (action: ResizeHandlerAction, isActive: boolean, event: ResizeEvent) => void;
|
|
5
4
|
export type PointerHitAreaMargins = {
|
|
6
5
|
coarse: number;
|
|
7
6
|
fine: number;
|
|
@@ -9,7 +9,9 @@ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement.js";
|
|
|
9
9
|
import { getResizeHandleElementIndex } from "./utils/dom/getResizeHandleElementIndex.js";
|
|
10
10
|
import { getResizeHandleElementsForGroup } from "./utils/dom/getResizeHandleElementsForGroup.js";
|
|
11
11
|
import { getResizeHandlePanelIds } from "./utils/dom/getResizeHandlePanelIds.js";
|
|
12
|
+
import { getIntersectingRectangle } from "./utils/rects/getIntersectingRectangle.js";
|
|
13
|
+
import { intersects } from "./utils/rects/intersects.js";
|
|
12
14
|
import type { ImperativePanelHandle, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps } from "./Panel.js";
|
|
13
15
|
import type { ImperativePanelGroupHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage } from "./PanelGroup.js";
|
|
14
16
|
import type { PanelResizeHandleOnDragging, PanelResizeHandleProps } from "./PanelResizeHandle.js";
|
|
15
|
-
export { ImperativePanelGroupHandle, ImperativePanelHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps, PanelResizeHandleOnDragging, PanelResizeHandleProps, Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, };
|
|
17
|
+
export { ImperativePanelGroupHandle, ImperativePanelHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps, PanelResizeHandleOnDragging, PanelResizeHandleProps, Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, intersects, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, };
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var stackingOrder = require('stacking-order');
|
|
6
7
|
|
|
7
8
|
function _interopNamespace(e) {
|
|
8
9
|
if (e && e.__esModule) return e;
|
|
@@ -294,6 +295,14 @@ function getInputType() {
|
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
function intersects(rectOne, rectTwo, strict) {
|
|
299
|
+
if (strict) {
|
|
300
|
+
return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
|
|
301
|
+
} else {
|
|
302
|
+
return rectOne.x <= rectTwo.x + rectTwo.width && rectOne.x + rectOne.width >= rectTwo.x && rectOne.y <= rectTwo.y + rectTwo.height && rectOne.y + rectOne.height >= rectTwo.y;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
297
306
|
const EXCEEDED_HORIZONTAL_MIN = 0b0001;
|
|
298
307
|
const EXCEEDED_HORIZONTAL_MAX = 0b0010;
|
|
299
308
|
const EXCEEDED_VERTICAL_MIN = 0b0100;
|
|
@@ -332,12 +341,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
|
|
|
332
341
|
};
|
|
333
342
|
}
|
|
334
343
|
function handlePointerDown(event) {
|
|
344
|
+
const {
|
|
345
|
+
target
|
|
346
|
+
} = event;
|
|
335
347
|
const {
|
|
336
348
|
x,
|
|
337
349
|
y
|
|
338
350
|
} = getResizeEventCoordinates(event);
|
|
339
351
|
isPointerDown = true;
|
|
340
352
|
recalculateIntersectingHandles({
|
|
353
|
+
target,
|
|
341
354
|
x,
|
|
342
355
|
y
|
|
343
356
|
});
|
|
@@ -352,29 +365,32 @@ function handlePointerMove(event) {
|
|
|
352
365
|
x,
|
|
353
366
|
y
|
|
354
367
|
} = getResizeEventCoordinates(event);
|
|
355
|
-
if (isPointerDown) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
} = data;
|
|
360
|
-
setResizeHandlerState("move", "drag", event);
|
|
361
|
-
});
|
|
368
|
+
if (!isPointerDown) {
|
|
369
|
+
const {
|
|
370
|
+
target
|
|
371
|
+
} = event;
|
|
362
372
|
|
|
363
|
-
//
|
|
364
|
-
|
|
365
|
-
|
|
373
|
+
// Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
|
|
374
|
+
// at that point, the handles may not move with the pointer (depending on constraints)
|
|
375
|
+
// but the same set of active handles should be locked until the pointer is released
|
|
366
376
|
recalculateIntersectingHandles({
|
|
377
|
+
target,
|
|
367
378
|
x,
|
|
368
379
|
y
|
|
369
380
|
});
|
|
370
|
-
updateResizeHandlerStates("move", event);
|
|
371
|
-
updateCursor();
|
|
372
381
|
}
|
|
382
|
+
updateResizeHandlerStates("move", event);
|
|
383
|
+
|
|
384
|
+
// Update cursor based on return value(s) from active handles
|
|
385
|
+
updateCursor();
|
|
373
386
|
if (intersectingHandles.length > 0) {
|
|
374
387
|
event.preventDefault();
|
|
375
388
|
}
|
|
376
389
|
}
|
|
377
390
|
function handlePointerUp(event) {
|
|
391
|
+
const {
|
|
392
|
+
target
|
|
393
|
+
} = event;
|
|
378
394
|
const {
|
|
379
395
|
x,
|
|
380
396
|
y
|
|
@@ -384,33 +400,72 @@ function handlePointerUp(event) {
|
|
|
384
400
|
if (intersectingHandles.length > 0) {
|
|
385
401
|
event.preventDefault();
|
|
386
402
|
}
|
|
403
|
+
updateResizeHandlerStates("up", event);
|
|
387
404
|
recalculateIntersectingHandles({
|
|
405
|
+
target,
|
|
388
406
|
x,
|
|
389
407
|
y
|
|
390
408
|
});
|
|
391
|
-
updateResizeHandlerStates("up", event);
|
|
392
409
|
updateCursor();
|
|
393
410
|
updateListeners();
|
|
394
411
|
}
|
|
395
412
|
function recalculateIntersectingHandles({
|
|
413
|
+
target,
|
|
396
414
|
x,
|
|
397
415
|
y
|
|
398
416
|
}) {
|
|
399
417
|
intersectingHandles.splice(0);
|
|
418
|
+
let targetElement = null;
|
|
419
|
+
if (target instanceof HTMLElement) {
|
|
420
|
+
targetElement = target;
|
|
421
|
+
}
|
|
400
422
|
registeredResizeHandlers.forEach(data => {
|
|
401
423
|
const {
|
|
402
|
-
element,
|
|
424
|
+
element: dragHandleElement,
|
|
403
425
|
hitAreaMargins
|
|
404
426
|
} = data;
|
|
427
|
+
const dragHandleRect = dragHandleElement.getBoundingClientRect();
|
|
405
428
|
const {
|
|
406
429
|
bottom,
|
|
407
430
|
left,
|
|
408
431
|
right,
|
|
409
432
|
top
|
|
410
|
-
} =
|
|
433
|
+
} = dragHandleRect;
|
|
411
434
|
const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
435
|
+
const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
|
|
436
|
+
if (eventIntersects) {
|
|
437
|
+
// TRICKY
|
|
438
|
+
// We listen for pointers events at the root in order to support hit area margins
|
|
439
|
+
// (determining when the pointer is close enough to an element to be considered a "hit")
|
|
440
|
+
// Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
|
|
441
|
+
// so at this point we need to compare stacking order of a potentially intersecting drag handle,
|
|
442
|
+
// and the element that was actually clicked/touched
|
|
443
|
+
if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
|
|
444
|
+
// Calculating stacking order has a cost, so we should avoid it if possible
|
|
445
|
+
// That is why we only check potentially intersecting handles,
|
|
446
|
+
// and why we skip if the event target is within the handle's DOM
|
|
447
|
+
stackingOrder.compare(targetElement, dragHandleElement) > 0) {
|
|
448
|
+
// If the target is above the drag handle, then we also need to confirm they overlap
|
|
449
|
+
// If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
|
|
450
|
+
//
|
|
451
|
+
// It's not enough to compare only the target
|
|
452
|
+
// The target might be a small element inside of a larger container
|
|
453
|
+
// (For example, a SPAN or a DIV inside of a larger modal dialog)
|
|
454
|
+
let currentElement = targetElement;
|
|
455
|
+
let didIntersect = false;
|
|
456
|
+
while (currentElement) {
|
|
457
|
+
if (currentElement.contains(dragHandleElement)) {
|
|
458
|
+
break;
|
|
459
|
+
} else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
|
|
460
|
+
didIntersect = true;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
currentElement = currentElement.parentElement;
|
|
464
|
+
}
|
|
465
|
+
if (didIntersect) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
414
469
|
intersectingHandles.push(data);
|
|
415
470
|
}
|
|
416
471
|
});
|
|
@@ -502,15 +557,8 @@ function updateResizeHandlerStates(action, event) {
|
|
|
502
557
|
const {
|
|
503
558
|
setResizeHandlerState
|
|
504
559
|
} = data;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
setResizeHandlerState(action, "drag", event);
|
|
508
|
-
} else {
|
|
509
|
-
setResizeHandlerState(action, "hover", event);
|
|
510
|
-
}
|
|
511
|
-
} else {
|
|
512
|
-
setResizeHandlerState(action, "inactive", event);
|
|
513
|
-
}
|
|
560
|
+
const isActive = intersectingHandles.includes(data);
|
|
561
|
+
setResizeHandlerState(action, isActive, event);
|
|
514
562
|
});
|
|
515
563
|
}
|
|
516
564
|
|
|
@@ -2047,6 +2095,12 @@ function PanelResizeHandle({
|
|
|
2047
2095
|
const [state, setState] = useState("inactive");
|
|
2048
2096
|
const [isFocused, setIsFocused] = useState(false);
|
|
2049
2097
|
const [resizeHandler, setResizeHandler] = useState(null);
|
|
2098
|
+
const committedValuesRef = useRef({
|
|
2099
|
+
state
|
|
2100
|
+
});
|
|
2101
|
+
useLayoutEffect(() => {
|
|
2102
|
+
committedValuesRef.current.state = state;
|
|
2103
|
+
});
|
|
2050
2104
|
useEffect(() => {
|
|
2051
2105
|
if (disabled) {
|
|
2052
2106
|
setResizeHandler(null);
|
|
@@ -2062,38 +2116,47 @@ function PanelResizeHandle({
|
|
|
2062
2116
|
}
|
|
2063
2117
|
const element = elementRef.current;
|
|
2064
2118
|
assert(element);
|
|
2065
|
-
const setResizeHandlerState = (action,
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
onDragging
|
|
2119
|
+
const setResizeHandlerState = (action, isActive, event) => {
|
|
2120
|
+
if (isActive) {
|
|
2121
|
+
switch (action) {
|
|
2122
|
+
case "down":
|
|
2123
|
+
{
|
|
2124
|
+
setState("drag");
|
|
2125
|
+
startDragging(resizeHandleId, event);
|
|
2126
|
+
const {
|
|
2127
|
+
onDragging
|
|
2128
|
+
} = callbacksRef.current;
|
|
2129
|
+
if (onDragging) {
|
|
2130
|
+
onDragging(true);
|
|
2131
|
+
}
|
|
2132
|
+
break;
|
|
2076
2133
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2134
|
+
case "move":
|
|
2135
|
+
{
|
|
2136
|
+
const {
|
|
2137
|
+
state
|
|
2138
|
+
} = committedValuesRef.current;
|
|
2139
|
+
if (state !== "drag") {
|
|
2140
|
+
setState("hover");
|
|
2141
|
+
}
|
|
2142
|
+
resizeHandler(event);
|
|
2143
|
+
break;
|
|
2087
2144
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2145
|
+
case "up":
|
|
2146
|
+
{
|
|
2147
|
+
setState("hover");
|
|
2148
|
+
stopDragging();
|
|
2149
|
+
const {
|
|
2150
|
+
onDragging
|
|
2151
|
+
} = callbacksRef.current;
|
|
2152
|
+
if (onDragging) {
|
|
2153
|
+
onDragging(false);
|
|
2154
|
+
}
|
|
2155
|
+
break;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
} else {
|
|
2159
|
+
setState("inactive");
|
|
2097
2160
|
}
|
|
2098
2161
|
};
|
|
2099
2162
|
return registerResizeHandle(resizeHandleId, element, direction, {
|
|
@@ -2150,10 +2213,28 @@ function getPanelElementsForGroup(groupId, scope = document) {
|
|
|
2150
2213
|
return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
|
|
2151
2214
|
}
|
|
2152
2215
|
|
|
2216
|
+
function getIntersectingRectangle(rectOne, rectTwo, strict) {
|
|
2217
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
2218
|
+
return {
|
|
2219
|
+
x: 0,
|
|
2220
|
+
y: 0,
|
|
2221
|
+
width: 0,
|
|
2222
|
+
height: 0
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
return {
|
|
2226
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
2227
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
2228
|
+
width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
|
|
2229
|
+
height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2153
2233
|
exports.Panel = Panel;
|
|
2154
2234
|
exports.PanelGroup = PanelGroup;
|
|
2155
2235
|
exports.PanelResizeHandle = PanelResizeHandle;
|
|
2156
2236
|
exports.assert = assert;
|
|
2237
|
+
exports.getIntersectingRectangle = getIntersectingRectangle;
|
|
2157
2238
|
exports.getPanelElement = getPanelElement;
|
|
2158
2239
|
exports.getPanelElementsForGroup = getPanelElementsForGroup;
|
|
2159
2240
|
exports.getPanelGroupElement = getPanelGroupElement;
|
|
@@ -2161,3 +2242,4 @@ exports.getResizeHandleElement = getResizeHandleElement;
|
|
|
2161
2242
|
exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
|
|
2162
2243
|
exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
|
|
2163
2244
|
exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
|
|
2245
|
+
exports.intersects = intersects;
|
|
@@ -3,11 +3,13 @@ export {
|
|
|
3
3
|
PanelGroup,
|
|
4
4
|
PanelResizeHandle,
|
|
5
5
|
assert,
|
|
6
|
+
getIntersectingRectangle,
|
|
6
7
|
getPanelElement,
|
|
7
8
|
getPanelElementsForGroup,
|
|
8
9
|
getPanelGroupElement,
|
|
9
10
|
getResizeHandleElement,
|
|
10
11
|
getResizeHandleElementIndex,
|
|
11
12
|
getResizeHandleElementsForGroup,
|
|
12
|
-
getResizeHandlePanelIds
|
|
13
|
+
getResizeHandlePanelIds,
|
|
14
|
+
intersects
|
|
13
15
|
} from "./react-resizable-panels.browser.cjs.js";
|