Skip to content

Commit 07ec90b

Browse files
author
Helen Le
committed
Revert "fix(menu): handle touch device submenu interactions"
This reverts commit 2a7a50b.
1 parent b73c357 commit 07ec90b

File tree

1 file changed

+52
-45
lines changed

1 file changed

+52
-45
lines changed

packages/menu/src/MenuItem.ts

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,6 @@ export class MenuItem extends LikeAnchor(
195195

196196
private _value = '';
197197

198-
private _lastPointerType?: string;
199-
200198
/**
201199
* @private
202200
* text content of the menu item minus whitespace
@@ -459,15 +457,25 @@ export class MenuItem extends LikeAnchor(
459457
}
460458
}
461459

460+
private handlePointerdown(event: PointerEvent): void {
461+
if (event.target === this && this.hasSubmenu && this.open) {
462+
this.addEventListener('focus', this.handleSubmenuFocus, {
463+
once: true,
464+
});
465+
this.overlayElement.addEventListener(
466+
'beforetoggle',
467+
this.handleBeforetoggle
468+
);
469+
}
470+
}
471+
462472
protected override firstUpdated(changes: PropertyValues): void {
463473
super.firstUpdated(changes);
464474
this.setAttribute('tabindex', '-1');
465475
this.addEventListener('keydown', this.handleKeydown);
466476
this.addEventListener('mouseover', this.handleMouseover);
467-
// Register pointerenter/leave for ALL menu items (not just those with submenus)
468-
// so items without submenus can close sibling submenus when hovered
469-
this.addEventListener('pointerenter', this.handlePointerenter);
470-
this.addEventListener('pointerleave', this.handlePointerleave);
477+
this.addEventListener('pointerdown', this.handlePointerdown);
478+
this.addEventListener('pointerenter', this.closeOverlaysForRoot);
471479
if (!this.hasAttribute('id')) {
472480
this.id = `sp-menu-item-${randomID()}`;
473481
}
@@ -586,6 +594,11 @@ export class MenuItem extends LikeAnchor(
586594
}
587595
};
588596

597+
protected closeOverlaysForRoot(): void {
598+
if (this.open) return;
599+
this.menuData.parentMenu?.closeDescendentOverlays();
600+
}
601+
589602
protected handleFocus(event: FocusEvent): void {
590603
const { target } = event;
591604
if (target === this) {
@@ -600,64 +613,48 @@ export class MenuItem extends LikeAnchor(
600613
}
601614
}
602615

603-
protected handleSubmenuTriggerClick(event: Event): void {
616+
protected handleSubmenuClick(event: Event): void {
604617
if (event.composedPath().includes(this.overlayElement)) {
605618
return;
606619
}
607-
608-
// If submenu is already open, toggle it closed
609-
if (this.open && this._lastPointerType === 'touch') {
610-
event.preventDefault();
611-
event.stopPropagation(); // Don't let parent menu handle this
612-
this.open = false;
613-
return;
614-
}
615-
616-
// All: open if closed
617-
if (!this.open) {
618-
event.preventDefault();
619-
event.stopImmediatePropagation();
620-
this.openOverlay(true);
621-
}
620+
this.openOverlay(true);
622621
}
623622

624-
protected handlePointerenter(event: PointerEvent): void {
625-
this._lastPointerType = event.pointerType; // Track pointer type
623+
protected handleSubmenuFocus(): void {
624+
requestAnimationFrame(() => {
625+
// Wait till after `closeDescendentOverlays` has happened in Menu
626+
// to reopen (keep open) the direct descendent of this Menu Item
627+
this.overlayElement.open = this.open;
628+
this.focused = false;
629+
});
630+
}
626631

627-
// For touch: don't handle pointerenter, let click handle it
628-
if (event.pointerType === 'touch') {
629-
return;
632+
protected handleBeforetoggle = (event: Event): void => {
633+
if ((event as Event & { newState: string }).newState === 'closed') {
634+
this.open = true;
635+
this.overlayElement.manuallyKeepOpen();
636+
this.overlayElement.removeEventListener(
637+
'beforetoggle',
638+
this.handleBeforetoggle
639+
);
630640
}
641+
};
631642

632-
// Close sibling submenus before opening this one
633-
this.menuData.parentMenu?.closeDescendentOverlays();
634-
643+
protected handlePointerenter(): void {
635644
if (this.leaveTimeout) {
636645
clearTimeout(this.leaveTimeout);
637646
delete this.leaveTimeout;
638647
this.recentlyLeftChild = false;
639648
return;
640649
}
641-
642-
// Only focus items with submenus on hover (to show they're interactive)
643-
// Regular items should not show focus styling on hover, only on keyboard navigation
644-
if (this.hasSubmenu) {
645-
this.focus();
646-
}
650+
this.focus();
647651
this.openOverlay();
648652
}
649653

650654
protected leaveTimeout?: ReturnType<typeof setTimeout>;
651655
protected recentlyLeftChild = false;
652656

653-
protected handlePointerleave(event: PointerEvent): void {
654-
this._lastPointerType = event.pointerType; // Update on leave too
655-
656-
// For touch: don't handle pointerleave, let click handle it
657-
if (event.pointerType === 'touch') {
658-
return;
659-
}
660-
657+
protected handlePointerleave(): void {
661658
this._closedViaPointer = true;
662659
if (this.open && !this.recentlyLeftChild) {
663660
this.leaveTimeout = setTimeout(() => {
@@ -785,7 +782,17 @@ export class MenuItem extends LikeAnchor(
785782
const options = { signal: this.abortControllerSubmenu.signal };
786783
this.addEventListener(
787784
'click',
788-
this.handleSubmenuTriggerClick,
785+
this.handleSubmenuClick,
786+
options
787+
);
788+
this.addEventListener(
789+
'pointerenter',
790+
this.handlePointerenter,
791+
options
792+
);
793+
this.addEventListener(
794+
'pointerleave',
795+
this.handlePointerleave,
789796
options
790797
);
791798
this.addEventListener(

0 commit comments

Comments
 (0)