@@ -309,6 +309,16 @@ export class MenuItem extends LikeAnchor(
309
309
@property ( { type : Boolean , reflect : true } )
310
310
public open = false ;
311
311
312
+ /**
313
+ * whether menu item's submenu is opened via keyboard
314
+ */
315
+ private _openedViaKeyboard = false ;
316
+
317
+ /**
318
+ * whether menu item's submenu is closed via pointer leave
319
+ */
320
+ private _closedViaPointer = false ;
321
+
312
322
private handleClickCapture ( event : Event ) : void | boolean {
313
323
if ( this . disabled ) {
314
324
event . preventDefault ( ) ;
@@ -369,12 +379,14 @@ export class MenuItem extends LikeAnchor(
369
379
import ( '@spectrum-web-components/popover/sp-popover.js' ) ;
370
380
return html `
371
381
< sp-overlay
382
+ receives-focus ="false "
372
383
.triggerElement =${ this as HTMLElement }
373
384
?disabled =${ ! this . hasSubmenu }
374
385
?open=${ this . hasSubmenu &&
375
386
this . open &&
376
387
this . dependencyManager . loaded }
377
388
.placement=${ this . isLTR ? 'right-start' : 'left-start' }
389
+ receives-focus="false"
378
390
.offset=${ [ - 10 , - 5 ] as [ number , number ] }
379
391
.type=${ 'auto' }
380
392
@close=${ ( event : Event ) => event . stopPropagation ( ) }
@@ -460,13 +472,20 @@ export class MenuItem extends LikeAnchor(
460
472
super . firstUpdated ( changes ) ;
461
473
this . setAttribute ( 'tabindex' , '-1' ) ;
462
474
this . addEventListener ( 'keydown' , this . handleKeydown ) ;
475
+ this . addEventListener ( 'mouseover' , this . handleMouseover ) ;
463
476
this . addEventListener ( 'pointerdown' , this . handlePointerdown ) ;
464
477
this . addEventListener ( 'pointerenter' , this . closeOverlaysForRoot ) ;
465
478
if ( ! this . hasAttribute ( 'id' ) ) {
466
479
this . id = `sp-menu-item-${ randomID ( ) } ` ;
467
480
}
468
481
}
469
-
482
+ handleMouseover ( event : MouseEvent ) : void {
483
+ const target = event . target as HTMLElement ;
484
+ if ( target === this ) {
485
+ this . focus ( ) ;
486
+ this . focused = false ;
487
+ }
488
+ }
470
489
/**
471
490
* forward key info from keydown event to parent menu
472
491
*/
@@ -509,7 +528,7 @@ export class MenuItem extends LikeAnchor(
509
528
if ( event . composedPath ( ) . includes ( this . overlayElement ) ) {
510
529
return ;
511
530
}
512
- this . openOverlay ( ) ;
531
+ this . openOverlay ( true ) ;
513
532
}
514
533
515
534
protected handleSubmenuFocus ( ) : void {
@@ -536,15 +555,18 @@ export class MenuItem extends LikeAnchor(
536
555
if ( this . leaveTimeout ) {
537
556
clearTimeout ( this . leaveTimeout ) ;
538
557
delete this . leaveTimeout ;
558
+ this . recentlyLeftChild = false ;
539
559
return ;
540
560
}
561
+ this . focus ( ) ;
541
562
this . openOverlay ( ) ;
542
563
}
543
564
544
565
protected leaveTimeout ?: ReturnType < typeof setTimeout > ;
545
566
protected recentlyLeftChild = false ;
546
567
547
568
protected handlePointerleave ( ) : void {
569
+ this . _closedViaPointer = true ;
548
570
if ( this . open && ! this . recentlyLeftChild ) {
549
571
this . leaveTimeout = setTimeout ( ( ) => {
550
572
delete this . leaveTimeout ;
@@ -570,38 +592,37 @@ export class MenuItem extends LikeAnchor(
570
592
}
571
593
572
594
protected async handleSubmenuPointerleave ( ) : Promise < void > {
573
- requestAnimationFrame ( ( ) => {
574
- this . recentlyLeftChild = false ;
575
- } ) ;
595
+ this . recentlyLeftChild = false ;
576
596
}
577
597
578
598
protected handleSubmenuOpen ( event : Event ) : void {
579
- const shouldFocus = this . matches ( ':focus, :focus-within' ) || this . focused ;
580
- this . focused = false ;
581
599
const parentOverlay = event . composedPath ( ) . find ( ( el ) => {
582
600
return (
583
601
el !== this . overlayElement &&
584
602
( el as HTMLElement ) . localName === 'sp-overlay'
585
603
) ;
586
604
} ) as Overlay ;
587
- if ( shouldFocus )
605
+ if ( this . _openedViaKeyboard ) {
588
606
this . submenuElement ?. focus ( ) ;
607
+ }
589
608
this . overlayElement . parentOverlayToForceClose = parentOverlay ;
590
609
}
591
610
592
611
protected cleanup ( ) : void {
612
+ this . _closedViaPointer = false ;
593
613
this . setAttribute ( 'aria-expanded' , 'false' ) ;
594
614
this . open = false ;
595
615
this . active = false ;
596
616
}
597
617
598
- public async openOverlay ( ) : Promise < void > {
618
+ public async openOverlay ( shouldFocus : boolean = false ) : Promise < void > {
599
619
if ( ! this . hasSubmenu || this . open || this . disabled ) {
600
620
return ;
601
621
}
602
622
this . open = true ;
603
623
this . active = true ;
604
624
this . setAttribute ( 'aria-expanded' , 'true' ) ;
625
+ this . _openedViaKeyboard = shouldFocus ;
605
626
this . addEventListener ( 'sp-closed' , this . cleanup , {
606
627
once : true ,
607
628
} ) ;
@@ -632,7 +653,8 @@ export class MenuItem extends LikeAnchor(
632
653
changes . has ( 'open' ) &&
633
654
! this . open &&
634
655
this . hasSubmenu &&
635
- this . hasVisibleFocusInTree ( )
656
+ ! this . _closedViaPointer &&
657
+ this . matches ( ':focus-within' )
636
658
) {
637
659
this . focus ( ) ;
638
660
}
0 commit comments