Skip to content

Commit 862bc01

Browse files
author
Shipra Gupta
committed
refactor: resolve code review comments
1 parent e8cf182 commit 862bc01

File tree

2 files changed

+57
-34
lines changed

2 files changed

+57
-34
lines changed

1st-gen/packages/menu/src/MenuItem.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,6 @@ export class MenuItem extends LikeAnchor(
181181
return this._value || this.itemText;
182182
}
183183

184-
private _lastPointerType?: string;
185-
186184
public set value(value: string) {
187185
if (value === this._value) {
188186
return;
@@ -322,6 +320,13 @@ export class MenuItem extends LikeAnchor(
322320
*/
323321
private _closedViaPointer = false;
324322

323+
/**
324+
* Touch interaction state for submenu toggling
325+
*/
326+
private _activePointerId?: number;
327+
private _touchHandledViaPointerup = false;
328+
private _touchAbortController?: AbortController;
329+
325330
private handleClickCapture(event: Event): void | boolean {
326331
if (this.disabled) {
327332
event.preventDefault();
@@ -459,57 +464,74 @@ export class MenuItem extends LikeAnchor(
459464
}
460465
}
461466

462-
private _touchListenerActive = false;
463-
464467
private handlePointerdown(event: PointerEvent): void {
465-
// Track pointer type for touch detection
466-
this._lastPointerType = event.pointerType;
468+
const path = event.composedPath();
469+
const targetIsInOverlay =
470+
this.overlayElement && path.includes(this.overlayElement);
467471

468-
// For touch devices with submenus, handle on pointerup instead of click
469-
// Only if the touch is directly on this menu item (not on overlay or child elements)
470472
if (
471473
event.pointerType === 'touch' &&
472474
this.hasSubmenu &&
473-
event.target === this &&
474-
!this._touchListenerActive
475+
!targetIsInOverlay &&
476+
this._activePointerId === undefined
475477
) {
476-
event.preventDefault(); // Prevent click suppression
477-
event.stopPropagation(); // Prevent bubbling to parent menu items
478-
this._touchListenerActive = true;
479-
this.addEventListener('pointerup', this.handleTouchSubmenuToggle, {
478+
this._activePointerId = event.pointerId;
479+
this._touchAbortController = new AbortController();
480+
481+
window.addEventListener(
482+
'pointerup',
483+
this.handleTouchSubmenuToggle,
484+
{ once: true, signal: this._touchAbortController.signal }
485+
);
486+
window.addEventListener('pointercancel', this.handleTouchCleanup, {
480487
once: true,
488+
signal: this._touchAbortController.signal,
481489
});
482490
}
483491

484492
if (
485-
event.target === this &&
493+
!targetIsInOverlay &&
486494
this.hasSubmenu &&
487495
this.open &&
488496
event.pointerType !== 'touch'
489497
) {
490498
this.addEventListener('focus', this.handleSubmenuFocus, {
491499
once: true,
492500
});
493-
this.overlayElement.addEventListener(
501+
this.overlayElement?.addEventListener(
494502
'beforetoggle',
495503
this.handleBeforetoggle
496504
);
497505
}
498506
}
499507

500508
private handleTouchSubmenuToggle = (event: PointerEvent): void => {
501-
event.preventDefault();
502-
event.stopPropagation();
509+
if (event.pointerId !== this._activePointerId) {
510+
return;
511+
}
503512

504-
// Reset the listener flag
505-
this._touchListenerActive = false;
513+
this._touchAbortController?.abort();
514+
this._touchHandledViaPointerup = true;
515+
this._activePointerId = undefined;
506516

507-
// Toggle the submenu
508517
if (this.open) {
509518
this.open = false;
510519
} else {
511-
this.openOverlay(true);
520+
this.openOverlay();
512521
}
522+
523+
setTimeout(() => {
524+
this._touchHandledViaPointerup = false;
525+
}, 0);
526+
};
527+
528+
private handleTouchCleanup = (event: PointerEvent): void => {
529+
if (event.pointerId !== this._activePointerId) {
530+
return;
531+
}
532+
this._touchAbortController?.abort();
533+
this._activePointerId = undefined;
534+
this._touchHandledViaPointerup = false;
513535
};
514536

515537
protected override firstUpdated(changes: PropertyValues): void {
@@ -657,14 +679,7 @@ export class MenuItem extends LikeAnchor(
657679
}
658680

659681
protected handleSubmenuClick(event: Event): void {
660-
const pointerEvent = event as PointerEvent;
661-
662-
const isTouchEvent =
663-
pointerEvent.pointerType === 'touch' ||
664-
this._lastPointerType === 'touch';
665-
666-
// For touch events, completely ignore click
667-
if (isTouchEvent) {
682+
if (this._touchHandledViaPointerup) {
668683
event.stopPropagation();
669684
event.preventDefault();
670685
return;
@@ -698,8 +713,6 @@ export class MenuItem extends LikeAnchor(
698713
};
699714

700715
protected handlePointerenter(event: PointerEvent): void {
701-
this._lastPointerType = event.pointerType;
702-
703716
// For touch devices, don't open on pointerenter - let click handle it
704717
if (event.pointerType === 'touch') {
705718
return;
@@ -719,8 +732,6 @@ export class MenuItem extends LikeAnchor(
719732
protected recentlyLeftChild = false;
720733

721734
protected handlePointerleave(event: PointerEvent): void {
722-
this._lastPointerType = event.pointerType;
723-
724735
// For touch devices, don't close on pointerleave - let click handle it
725736
if (event.pointerType === 'touch') {
726737
return;
@@ -892,6 +903,12 @@ export class MenuItem extends LikeAnchor(
892903
selectionRoot: undefined,
893904
cleanupSteps: [],
894905
};
906+
907+
// Clean up any active touch listeners
908+
this._touchAbortController?.abort();
909+
this._activePointerId = undefined;
910+
this._touchHandledViaPointerup = false;
911+
895912
super.disconnectedCallback();
896913
}
897914

1st-gen/packages/menu/test/submenu.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,13 +1123,15 @@ describe('Submenu', () => {
11231123
new PointerEvent('pointerdown', {
11241124
bubbles: true,
11251125
pointerType: 'touch',
1126+
pointerId: 1,
11261127
})
11271128
);
11281129
const parentOpened = oneEvent(rootItem, 'sp-opened');
11291130
rootItem.dispatchEvent(
11301131
new PointerEvent('pointerup', {
11311132
bubbles: true,
11321133
pointerType: 'touch',
1134+
pointerId: 1,
11331135
})
11341136
);
11351137
await parentOpened;
@@ -1140,13 +1142,15 @@ describe('Submenu', () => {
11401142
new PointerEvent('pointerdown', {
11411143
bubbles: true,
11421144
pointerType: 'touch',
1145+
pointerId: 2,
11431146
})
11441147
);
11451148
const childOpened = oneEvent(childItem, 'sp-opened');
11461149
childItem.dispatchEvent(
11471150
new PointerEvent('pointerup', {
11481151
bubbles: true,
11491152
pointerType: 'touch',
1153+
pointerId: 2,
11501154
})
11511155
);
11521156
await childOpened;
@@ -1158,13 +1162,15 @@ describe('Submenu', () => {
11581162
new PointerEvent('pointerdown', {
11591163
bubbles: true,
11601164
pointerType: 'touch',
1165+
pointerId: 2,
11611166
})
11621167
);
11631168
const childClosed = oneEvent(childItem, 'sp-closed');
11641169
childItem.dispatchEvent(
11651170
new PointerEvent('pointerup', {
11661171
bubbles: true,
11671172
pointerType: 'touch',
1173+
pointerId: 2,
11681174
})
11691175
);
11701176
await childClosed;

0 commit comments

Comments
 (0)