@@ -72,6 +72,22 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
72
72
73
73
protected rovingTabindexController ?: RovingTabindexController < MenuItem > ;
74
74
75
+ private touchStartY : number | undefined = undefined ;
76
+ private touchStartTime : number | undefined = undefined ;
77
+ private isCurrentlyScrolling = false ;
78
+
79
+ private scrollThreshold = 10 ; // pixels
80
+ private scrollTimeThreshold = 300 ; // milliseconds
81
+
82
+ public get isScrolling ( ) : boolean {
83
+ return this . isCurrentlyScrolling ;
84
+ }
85
+
86
+ public set isScrolling ( value : boolean ) {
87
+ // For testing purposes, allow setting the scrolling state
88
+ this . isCurrentlyScrolling = value ;
89
+ }
90
+
75
91
/**
76
92
* label of the menu
77
93
*/
@@ -400,6 +416,13 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
400
416
this . addEventListener ( 'pointerup' , this . handlePointerup ) ;
401
417
this . addEventListener ( 'sp-opened' , this . handleSubmenuOpened ) ;
402
418
this . addEventListener ( 'sp-closed' , this . handleSubmenuClosed ) ;
419
+
420
+ this . addEventListener ( 'touchstart' , this . handleTouchStart , {
421
+ passive : true ,
422
+ } ) ;
423
+ this . addEventListener ( 'touchmove' , this . handleTouchMove , {
424
+ passive : true ,
425
+ } ) ;
403
426
}
404
427
405
428
/**
@@ -443,6 +466,42 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
443
466
}
444
467
}
445
468
469
+ private handleTouchStart ( event : TouchEvent ) : void {
470
+ if ( event . touches . length === 1 ) {
471
+ this . touchStartY = event . touches [ 0 ] . clientY ;
472
+ this . touchStartTime = Date . now ( ) ;
473
+ this . isCurrentlyScrolling = false ;
474
+ }
475
+ }
476
+
477
+ private handleTouchMove ( event : TouchEvent ) : void {
478
+ if (
479
+ event . touches . length === 1 &&
480
+ this . touchStartY !== undefined &&
481
+ this . touchStartTime !== undefined
482
+ ) {
483
+ const currentY = event . touches [ 0 ] . clientY ;
484
+ const deltaY = Math . abs ( currentY - this . touchStartY ) ;
485
+ const deltaTime = Date . now ( ) - this . touchStartTime ;
486
+
487
+ if (
488
+ deltaY > this . scrollThreshold &&
489
+ deltaTime < this . scrollTimeThreshold
490
+ ) {
491
+ this . isCurrentlyScrolling = true ;
492
+ }
493
+ }
494
+ }
495
+
496
+ private handleTouchEnd ( ) : void {
497
+ // Reset scrolling state after a short delay
498
+ setTimeout ( ( ) => {
499
+ this . isCurrentlyScrolling = false ;
500
+ this . touchStartY = undefined ;
501
+ this . touchStartTime = undefined ;
502
+ } , 100 ) ;
503
+ }
504
+
446
505
// if the click and pointerup events are on the same target, we should not
447
506
// handle the click event.
448
507
private pointerUpTarget = null as EventTarget | null ;
@@ -461,6 +520,7 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
461
520
}
462
521
463
522
private handlePointerup ( event : Event ) : void {
523
+ this . handleTouchEnd ( ) ;
464
524
/*
465
525
* early return if drag and select is not supported
466
526
* in this case, selection will be handled by the click event
@@ -478,6 +538,10 @@ export class Menu extends SizedMixin(SpectrumElement, { noDefaultSize: true }) {
478
538
return ;
479
539
}
480
540
541
+ if ( this . isScrolling ) {
542
+ return ;
543
+ }
544
+
481
545
const path = event . composedPath ( ) ;
482
546
const target = path . find ( ( el ) => {
483
547
/* c8 ignore next 3 */
0 commit comments