Skip to content

Commit b9126cc

Browse files
cpsoinosclaude
andcommitted
refactor(menu): clean up shadow DOM implementation
- Remove unnecessary empty lines and unused variables - Extract shadow DOM detection into reusable utility function - Simplify coordinate handling by removing unused adjustments - Revert isPointerInGraceArea changes that weren't needed for the fix - Reduce code duplication in shadow DOM detection logic 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent c123d11 commit b9126cc

File tree

1 file changed

+15
-34
lines changed

1 file changed

+15
-34
lines changed

packages/react/menu/src/menu.tsx

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,7 @@ const MenuContentImpl = React.forwardRef<MenuContentImplElement, MenuContentImpl
443443
} else {
444444
// In shadow DOM, force close other submenus when entering any menu item
445445
const target = event.target as Element;
446-
const isInShadowDOM = target && target.getRootNode() !== document && 'host' in target.getRootNode();
447-
if (isInShadowDOM) {
446+
if (isInShadowDOM(target)) {
448447
const menuItem = event.currentTarget as HTMLElement;
449448

450449
// Clear grace intent
@@ -755,7 +754,6 @@ const MenuItemImpl = React.forwardRef<MenuItemImplElement, MenuItemImplProps>(
755754
if (!event.defaultPrevented) {
756755
const item = event.currentTarget;
757756
item.focus({ preventScroll: true });
758-
759757
}
760758
}
761759
})
@@ -1154,49 +1152,35 @@ const MenuSubTrigger = React.forwardRef<MenuSubTriggerElement, MenuSubTriggerPro
11541152
const contentNearEdge = contentRect[rightSide ? 'left' : 'right'];
11551153
const contentFarEdge = contentRect[rightSide ? 'right' : 'left'];
11561154

1157-
// In shadow DOM, we may need to adjust coordinates to ensure
1158-
// both the mouse position and rectangle are in the same coordinate system
1159-
let adjustedClientX = event.clientX;
1160-
let adjustedClientY = event.clientY;
1161-
const adjustedContentRect = contentRect;
1162-
1155+
// Check if we're in shadow DOM for adjusted behavior
11631156
const eventTarget = event.target as Element;
1164-
const isInShadowDOM = eventTarget && eventTarget.getRootNode() !== document && 'host' in eventTarget.getRootNode();
1165-
1166-
if (isInShadowDOM && context.content) {
1167-
// Use native event coordinates for more reliable positioning in shadow DOM
1168-
const nativeEvent = event.nativeEvent;
1169-
if (nativeEvent) {
1170-
adjustedClientX = nativeEvent.clientX;
1171-
adjustedClientY = nativeEvent.clientY;
1172-
}
1173-
}
1157+
const shadowDOM = isInShadowDOM(eventTarget);
11741158

11751159
contentContext.onPointerGraceIntentChange({
11761160
area: [
11771161
// Apply a bleed on clientX to ensure that our exit point is
11781162
// consistently within polygon bounds
1179-
{ x: adjustedClientX + bleed, y: adjustedClientY },
1180-
{ x: contentNearEdge, y: adjustedContentRect.top },
1181-
{ x: contentFarEdge, y: adjustedContentRect.top },
1182-
{ x: contentFarEdge, y: adjustedContentRect.bottom },
1183-
{ x: contentNearEdge, y: adjustedContentRect.bottom },
1163+
{ x: event.clientX + bleed, y: event.clientY },
1164+
{ x: contentNearEdge, y: contentRect.top },
1165+
{ x: contentFarEdge, y: contentRect.top },
1166+
{ x: contentFarEdge, y: contentRect.bottom },
1167+
{ x: contentNearEdge, y: contentRect.bottom },
11841168
],
11851169
side,
11861170
});
11871171

11881172
window.clearTimeout(pointerGraceTimerRef.current);
11891173

11901174
// Use longer grace period in shadow DOM since coordinate detection may be less reliable
1191-
const gracePeriod = isInShadowDOM ? 800 : 300;
1175+
const gracePeriod = shadowDOM ? 800 : 300;
11921176

11931177
pointerGraceTimerRef.current = window.setTimeout(
11941178
() => {
11951179
contentContext.onPointerGraceIntentChange(null);
11961180

11971181
// In shadow DOM, don't automatically close submenu on grace timer expiry
11981182
// Only close via explicit menu item selection logic
1199-
if (!isInShadowDOM && context.open) {
1183+
if (!shadowDOM && context.open) {
12001184
// Normal behavior for non-shadow DOM
12011185
context.onOpenChange(false);
12021186
}
@@ -1404,17 +1388,14 @@ function isPointInPolygon(point: Point, polygon: Polygon) {
14041388

14051389
function isPointerInGraceArea(event: React.PointerEvent, area?: Polygon) {
14061390
if (!area) return false;
1407-
1408-
// Use the most reliable coordinates available
1409-
const nativeEvent = event.nativeEvent;
1410-
const cursorPos = {
1411-
x: nativeEvent ? nativeEvent.clientX : event.clientX,
1412-
y: nativeEvent ? nativeEvent.clientY : event.clientY
1413-
};
1414-
1391+
const cursorPos = { x: event.clientX, y: event.clientY };
14151392
return isPointInPolygon(cursorPos, area);
14161393
}
14171394

1395+
function isInShadowDOM(element: Element): boolean {
1396+
return element && element.getRootNode() !== document && 'host' in element.getRootNode();
1397+
}
1398+
14181399
function whenMouse<E>(handler: React.PointerEventHandler<E>): React.PointerEventHandler<E> {
14191400
return (event) => (event.pointerType === 'mouse' ? handler(event) : undefined);
14201401
}

0 commit comments

Comments
 (0)