react-resizable-panels
Advanced tools
Comparing version 2.0.4 to 2.0.5
# Changelog | ||
## 2.0.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) | ||
## 2.0.4 | ||
@@ -4,0 +8,0 @@ |
@@ -12,5 +12,7 @@ import { Panel } from "./Panel.js"; | ||
import { getResizeHandlePanelIds } from "./utils/dom/getResizeHandlePanelIds.js"; | ||
import { getIntersectingRectangle } from "./utils/rects/getIntersectingRectangle.js"; | ||
import { intersects } from "./utils/rects/intersects.js"; | ||
import type { ImperativePanelHandle, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps } from "./Panel.js"; | ||
import type { ImperativePanelGroupHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage } from "./PanelGroup.js"; | ||
import type { PanelResizeHandleOnDragging, PanelResizeHandleProps } from "./PanelResizeHandle.js"; | ||
export { ImperativePanelGroupHandle, ImperativePanelHandle, PanelGroupOnLayout, PanelGroupProps, PanelGroupStorage, PanelOnCollapse, PanelOnExpand, PanelOnResize, PanelProps, PanelResizeHandleOnDragging, PanelResizeHandleProps, Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, }; | ||
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, }; |
{ | ||
"name": "react-resizable-panels", | ||
"version": "2.0.4", | ||
"version": "2.0.5", | ||
"description": "React components for resizable panel groups/layouts", | ||
@@ -69,2 +69,5 @@ "author": "Brian Vaughn <brian.david.vaughn@gmail.com>", | ||
}, | ||
"dependencies": { | ||
"stacking-order": "^1" | ||
}, | ||
"devDependencies": { | ||
@@ -71,0 +74,0 @@ "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", |
@@ -12,2 +12,4 @@ import { Panel } from "./Panel"; | ||
import { getResizeHandlePanelIds } from "./utils/dom/getResizeHandlePanelIds"; | ||
import { getIntersectingRectangle } from "./utils/rects/getIntersectingRectangle"; | ||
import { intersects } from "./utils/rects/intersects"; | ||
@@ -53,2 +55,4 @@ import type { | ||
assert, | ||
getIntersectingRectangle, | ||
intersects, | ||
@@ -55,0 +59,0 @@ // DOM helpers |
@@ -0,1 +1,2 @@ | ||
import { compare } from "stacking-order"; | ||
import { Direction, ResizeEvent } from "./types"; | ||
@@ -5,2 +6,3 @@ import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor"; | ||
import { getInputType } from "./utils/getInputType"; | ||
import { intersects } from "./utils/rects/intersects"; | ||
@@ -79,2 +81,3 @@ export type ResizeHandlerAction = "down" | "move" | "up"; | ||
function handlePointerDown(event: ResizeEvent) { | ||
const { target } = event; | ||
const { x, y } = getResizeEventCoordinates(event); | ||
@@ -84,3 +87,3 @@ | ||
recalculateIntersectingHandles({ x, y }); | ||
recalculateIntersectingHandles({ target, x, y }); | ||
updateListeners(); | ||
@@ -99,6 +102,8 @@ | ||
if (!isPointerDown) { | ||
const { target } = event; | ||
// Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed | ||
// at that point, the handles may not move with the pointer (depending on constraints) | ||
// but the same set of active handles should be locked until the pointer is released | ||
recalculateIntersectingHandles({ x, y }); | ||
recalculateIntersectingHandles({ target, x, y }); | ||
} | ||
@@ -117,2 +122,3 @@ | ||
function handlePointerUp(event: ResizeEvent) { | ||
const { target } = event; | ||
const { x, y } = getResizeEventCoordinates(event); | ||
@@ -127,4 +133,4 @@ | ||
recalculateIntersectingHandles({ x, y }); | ||
updateResizeHandlerStates("up", event); | ||
recalculateIntersectingHandles({ target, x, y }); | ||
updateCursor(); | ||
@@ -135,9 +141,24 @@ | ||
function recalculateIntersectingHandles({ x, y }: { x: number; y: number }) { | ||
function recalculateIntersectingHandles({ | ||
target, | ||
x, | ||
y, | ||
}: { | ||
target: EventTarget | null; | ||
x: number; | ||
y: number; | ||
}) { | ||
intersectingHandles.splice(0); | ||
let targetElement: HTMLElement | null = null; | ||
if (target instanceof HTMLElement) { | ||
targetElement = target; | ||
} | ||
registeredResizeHandlers.forEach((data) => { | ||
const { element, hitAreaMargins } = data; | ||
const { bottom, left, right, top } = element.getBoundingClientRect(); | ||
const { element: dragHandleElement, hitAreaMargins } = data; | ||
const dragHandleRect = dragHandleElement.getBoundingClientRect(); | ||
const { bottom, left, right, top } = dragHandleRect; | ||
const margin = isCoarsePointer | ||
@@ -147,3 +168,3 @@ ? hitAreaMargins.coarse | ||
const intersects = | ||
const eventIntersects = | ||
x >= left - margin && | ||
@@ -154,3 +175,49 @@ x <= right + margin && | ||
if (intersects) { | ||
if (eventIntersects) { | ||
// TRICKY | ||
// We listen for pointers events at the root in order to support hit area margins | ||
// (determining when the pointer is close enough to an element to be considered a "hit") | ||
// Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though | ||
// so at this point we need to compare stacking order of a potentially intersecting drag handle, | ||
// and the element that was actually clicked/touched | ||
if ( | ||
targetElement !== null && | ||
dragHandleElement !== targetElement && | ||
!dragHandleElement.contains(targetElement) && | ||
!targetElement.contains(dragHandleElement) && | ||
// Calculating stacking order has a cost, so we should avoid it if possible | ||
// That is why we only check potentially intersecting handles, | ||
// and why we skip if the event target is within the handle's DOM | ||
compare(targetElement, dragHandleElement) > 0 | ||
) { | ||
// If the target is above the drag handle, then we also need to confirm they overlap | ||
// If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive | ||
// | ||
// It's not enough to compare only the target | ||
// The target might be a small element inside of a larger container | ||
// (For example, a SPAN or a DIV inside of a larger modal dialog) | ||
let currentElement: HTMLElement | null = targetElement; | ||
let didIntersect = false; | ||
while (currentElement) { | ||
if (currentElement.contains(dragHandleElement)) { | ||
break; | ||
} else if ( | ||
intersects( | ||
currentElement.getBoundingClientRect(), | ||
dragHandleRect, | ||
true | ||
) | ||
) { | ||
didIntersect = true; | ||
break; | ||
} | ||
currentElement = currentElement.parentElement; | ||
} | ||
if (didIntersect) { | ||
return; | ||
} | ||
} | ||
intersectingHandles.push(data); | ||
@@ -157,0 +224,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1128268
111
32668
3
+ Addedstacking-order@^1
+ Addedstacking-order@1.0.1(transitive)