Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using mat-menu as context menu: How to close opened mat-menu, and open it in another location

Angular: v8.2.8

I manage to use mat-menu (angular material) to emulate the context menu. Right-clicking anyway on my page, the menu appears at my mouse location.

What I want to achieve: when I move my mouse to another location on the page, I right-click again. I was expecting the menu at the original location to close and reopen at the new location.

But In reality: When I right-click, nothing happens and the menu at the original location remains open.

In fact, I have to left-click to close the opened menu at original location, followed by right click to open menu at the new mouse location.

Question: Any idea how to achieve what I want?

@Component({
  selector: 'fl-home',
  template:`
 <mat-menu #contextmenu>
      <div >
           <button mat-menu-item>Clear all Paths</button>
       </div>
  </mat-menu>
  <div [matMenuTriggerFor]="contextmenu" [style.position]="'absolute'" [style.left.px]="menuX" [style.top.px]="menuY" ></div>
  
  <div class="container" (contextmenu)="onTriggerContextMenu($event);"> ....</div>
`})
export class HomeComponent  {

    menuX:number=0
    menuY:number=0

    @ViewChild(MatMenuTrigger,{static:false}) menu: MatMenuTrigger; 
    
   
    onTriggerContextMenu(event){
        
      event.preventDefault();
      this.menuX = event.x - 10;
      this.menuY = event.y - 10;
      this.menu.closeMenu() // close existing menu first.
      this.menu.openMenu()

    }
}
like image 414
Bruce Wills Avatar asked Oct 11 '19 16:10

Bruce Wills


2 Answers

Just add hasBackdrop=false as in <mat-menu ... [hasBackdrop]="false">. What happens is that when the menu is opened after I right click, an invisible backdrop overlay appears covering the entire page. No matter what I try to right click again, the contextmenu event won't trigger and hence onTriggerContextMenu() does not get called. By setting hasBackgrop to false, this overlay will not appear.

like image 152
Bruce Wills Avatar answered Sep 28 '22 04:09

Bruce Wills


My solution

  ngOnInit(): void {
    this.windowClickSubscription = fromEvent(window, 'click').subscribe((_) => {
      if (this.contextMenu.menuOpen) {
        this.contextMenu.closeMenu();
      }
    });
  }
  ngOnDestroy(): void {
    this.windowClickSubscription && this.windowClickSubscription.unsubscribe();
    this.menuSubscription && this.menuSubscription.unsubscribe();
  }
  onContextMenu(event: MouseEvent){
    event.preventDefault();
    this.menuSubscription && this.menuSubscription.unsubscribe();
    this.menuSubscription = of(1)
      .pipe(
        tap(() => {
          if (this.contextMenu.menuOpen) {
            this.contextMenu.closeMenu();
          }
          this.contextMenuPosition.x = event.clientX;
          this.contextMenuPosition.y = event.clientY;
        }),
        // delay(this.contextMenu.menuOpen ? 200 : 0),
        delayWhen((_) => (this.contextMenu.menuOpen ? interval(200) : of(undefined))),
        tap(async() => {
          this.contextMenu.openMenu();
          let backdrop: HTMLElement = null;
          do {
            await this.delay(100);
            backdrop = document.querySelector(
              'div.cdk-overlay-backdrop.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing'
            ) as HTMLElement;
          } while (backdrop === null);
          backdrop.style.pointerEvents = 'none';
        })
      )
      .subscribe();
  }
  delay(delayInms: number) {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(2);
      }, delayInms);
    });
  }

https://stackblitz.com/edit/angular-ivy-udzyez

like image 38
Jay Avatar answered Sep 28 '22 04:09

Jay