中心Mat-Menu,中心MatMenu与按钮叠加 [英] Center Mat-Menu, Center MatMenu Overlay to Button

查看:158
本文介绍了中心Mat-Menu,中心MatMenu与按钮叠加的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每个

解决方案

我选择了一个指令,因为 MatMenuTrigger 指令,复制材料源使用的逻辑很有意义。



到目前为止,这是我想出的方法,我不确定进入这样的类本身是否可以接受,或者不确定是否有这样做的负面影响。


对于这种建议或改进,我愿意就此方法进行建设性讨论。







Stackblitz



https://stackblitz.com/edit/angular- jpgjdc-nqmyht?embed = 1& fi le = app / center-matmenu.directive.ts



本质上来说,我将 matMenuTrigger 与通过将按钮放置在 div 包裹 mat-menu 的按钮上...这是我可以通过编程方式打开




  • 我还将 menuTrigger 分配给 div 上的 templateRef 并将其作为输入传递给我的 center-mat-menu 选择器




 <按钮mat-button [center-mat-menu] = menuTrigger> Menu< / button> 
< div#menuTrigger = matMenuTrigger [matMenuTriggerFor] =菜单>
< mat-menu#menu = matMenu>


在这里我通过 @HostListener

  @HostListener('click',['$ event'])

然后将 MatMenuTrigger 指令的逻辑复制到



这基本上是 openMenu() menu-trigger.ts 源代码中的c $ c>方法,除了在菜单初始化之后以及调用 this.menuTrigger.menu ['_ startAnimation'](); 打开菜单。



menu-trigger.ts


我将源按钮的尺寸存储到变量中,然后
使用计算按钮的中心点,然后将其与初始化菜单的
宽度一起使用,以计算 left




  @Directive({
选择器:'[center-mat-menu]'
})
出口类CenterMatmenuDirective {
overlayRef:OverlayRef;
overlayConf:OverlayConfig;
dropDown:HTMLElement;
overlayPositionBox:HTMLElement;
菜单:MatMenuPanel;
按钮:HTMLElement;
buttonWidth:数字;
buttonLeft:数字;
按钮底部:数字;

@Input(’center-mat-menu’)私人菜单触发:MatMenuTrigger;

构造函数(私有_menuButton:ElementRef,私有_renderer:Renderer2){}

@HostListener('click',['$ event'])
onclick( e){
this._setVariables();
//菜单不是通过键盘的向下箭头打开的,必须进行设置,以便MatMenuTrigger知道菜单是通过鼠标单击打开的。
this.menuTrigger [’_ openedBy’] = e.button === 0吗? mouse:null;

this._overrideMatMenu();

this.dropDown = this.overlayRef.overlayElement.children [0] .children [0]作为HTMLElement;
this.overlayPositionBox = this.overlayRef.hostElement;

setTimeout(()=> {
this._styleDropDown(this.dropDown);
this._setOverlayPosition(this.dropDown,this.overlayPositionBox);
this._openMenu();
})
}

private _setVariables(){
const config = this.menuTrigger ['_ getOverlayConfig']();
this.menuTrigger ['_ overlayRef'] = this.menuTrigger ['_ overlay']。create(config);
this.overlayRef = this.menuTrigger [’_ overlayRef’];
this.overlayConf = this.overlayRef.getConfig();
this.overlayRef.keydownEvents()。subscribe();
this.menu = this.menuTrigger.menu;
this._setButtonVars();
}

private _setButtonVars(){
this.button = this._menuButton.nativeElement;
this.buttonWidth = this.button.getBoundingClientRect()。width;
this.buttonLeft = this.button.getBoundingClientRect()。left;
this.buttonBottom = this.button.getBoundingClientRect()。bottom;
}

private _overrideMatMenu(){
let strat = this.overlayConf.positionStrategy as FlexibleConnectedPositionStrategy;
this.menuTrigger ['_ setPosition'](strat);
strat.positionChanges.subscribe(()=> {
this._setButtonVars();
this._setOverlayPosition(this.dropDown,this.overlayPositionBox);
})
this.overlayConf.hasBackdrop = this.menu.hasBackdrop == null吗?
!this.menuTrigger.triggersSubmenu():this.menu.hasBackdrop;
this.overlayRef.attach(this.menuTrigger [’_ getPortal']());

if(this.menu.lazyContent){
this.menu.lazyContent.attach()
}

this.menuTrigger ['_ closeSubscription' ] = this.menuTrigger ['_ menuClosingActions']()。subscribe(()=> {
this.menuTrigger.closeMenu();
});
this.menuTrigger [’_ initMenu’]();
}

private _styleDropDown(dropDown:HTMLElement){
this._renderer.setStyle(this._renderer.parentNode(dropDown),'transform-origin','center top 0px ');
}

private _setOverlayPosition(dropDown:HTMLElement,overlayPositionBox:HTMLElement){
let dropDownleft =((this.buttonWidth / 2 + this.buttonLeft)-dropDown.offsetWidth / 2 );

this._renderer.setStyle(overlayPositionBox,'top',this.buttonBottom + 5 +'px');
this._renderer.setStyle(overlayPositionBox,'left',dropDownleft +'px');
this._renderer.setStyle(overlayPositionBox,'height','100%');
}

private _openMenu(){
this.menuTrigger.menu [’_ startAnimation’]();
}
}


Per Material Issue 9631 centering the mat-menu to the button is as follows.

the issue is that supporting it deviates from the spec and may end up looking weird.


I have a need for this functionality... Because writing my own CDK overlay would be more time intensive than overriding the mat-menu component... I am not interested in re-creating the mat-menu, and simply need the menu to be centered... I am also not interested in any other library's to accomplish this, I want to use Material mat-menu, so my question is as follows.

Question:

Utilizing an angular directive, how can I override the private variables and methods of the MatMenuTrigger to center the CDK overlay to the button?

解决方案

I chose a Directive because MatMenuTrigger is a Directive, it made sense to replicate the logic used by the Material source.

So far this is the approach I have come up with, I am not sure if reaching into the class like this is "acceptable" per se, or if there are any negative ramifications for doing it this way.

I am open to constructive discussion on this approach for any recommendations or improvements.


Stackblitz

https://stackblitz.com/edit/angular-jpgjdc-nqmyht?embed=1&file=app/center-matmenu.directive.ts

Essentially I am decoupling the matMenuTrigger from the button by placing it on a div wrapping the mat-menu... this is so I can programmatically open the menu from the directive and not the button.

  • I am also assigning menuTrigger to a templateRef on the div and passing it as an input to my center-mat-menu selector

<button mat-button [center-mat-menu]="menuTrigger">Menu</button>
<div #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="menu">
 <mat-menu #menu="matMenu">

From there I am creating a listener via @HostListener

@HostListener('click', ['$event'])

And then replicating the logic of the MatMenuTrigger directive to manipulation the placement, initialize the menu, and fire the open animation on click.

This is basically a replication of the openMenu() method in the menu-trigger.ts source except I am manipulating the left and top styles after the menu is initialized, and before I call this.menuTrigger.menu['_startAnimation'](); to open the menu.

menu-trigger.ts

I store the dimension of the source button into variables and use that information to calculate the center point of the button, I then use that with the width of the initialized menu to calculate left

@Directive({
  selector: '[center-mat-menu]'
})
export class CenterMatmenuDirective {
  overlayRef: OverlayRef;
  overlayConf: OverlayConfig;
  dropDown: HTMLElement;
  overlayPositionBox: HTMLElement;
  menu: MatMenuPanel;
  button: HTMLElement;
  buttonWidth: number;
  buttonLeft: number;
  buttonBottom: number;

  @Input('center-mat-menu') private menuTrigger: MatMenuTrigger;

  constructor(private _menuButton: ElementRef, private _renderer: Renderer2) { }

  @HostListener('click', ['$event'])
  onclick(e) {
    this._setVariables();
    //menu not opened by keyboard down arrow, have to set this so MatMenuTrigger knows the menu was opened with a mouse click
    this.menuTrigger['_openedBy'] = e.button === 0 ? 'mouse' : null;

    this._overrideMatMenu();

    this.dropDown = this.overlayRef.overlayElement.children[0].children[0] as HTMLElement;
    this.overlayPositionBox = this.overlayRef.hostElement;

    setTimeout(() => {
      this._styleDropDown(this.dropDown);
      this._setOverlayPosition(this.dropDown, this.overlayPositionBox);
      this._openMenu();
    })
  }

  private _setVariables() {
    const config = this.menuTrigger['_getOverlayConfig']();
    this.menuTrigger['_overlayRef'] = this.menuTrigger['_overlay'].create(config);
    this.overlayRef = this.menuTrigger['_overlayRef'];
    this.overlayConf = this.overlayRef.getConfig();
    this.overlayRef.keydownEvents().subscribe();
    this.menu = this.menuTrigger.menu;
    this._setButtonVars();
  }

  private _setButtonVars() {
    this.button = this._menuButton.nativeElement;
    this.buttonWidth = this.button.getBoundingClientRect().width;
    this.buttonLeft = this.button.getBoundingClientRect().left;
    this.buttonBottom = this.button.getBoundingClientRect().bottom;
  }

  private _overrideMatMenu() {
    let strat = this.overlayConf.positionStrategy as FlexibleConnectedPositionStrategy;
    this.menuTrigger['_setPosition'](strat);
    strat.positionChanges.subscribe(() => {
      this._setButtonVars();
      this._setOverlayPosition(this.dropDown, this.overlayPositionBox);
    })
    this.overlayConf.hasBackdrop = this.menu.hasBackdrop == null ?
      !this.menuTrigger.triggersSubmenu() : this.menu.hasBackdrop;
    this.overlayRef.attach(this.menuTrigger['_getPortal']());

    if (this.menu.lazyContent) {
      this.menu.lazyContent.attach()
    }

    this.menuTrigger['_closeSubscription'] = this.menuTrigger['_menuClosingActions']().subscribe(() => {
      this.menuTrigger.closeMenu();
    });
    this.menuTrigger['_initMenu']();
  }

  private _styleDropDown(dropDown: HTMLElement) {
    this._renderer.setStyle(this._renderer.parentNode(dropDown), 'transform-origin', 'center top 0px');
  }

  private _setOverlayPosition(dropDown: HTMLElement, overlayPositionBox: HTMLElement) {
    let dropDownleft = ((this.buttonWidth / 2 + this.buttonLeft) - dropDown.offsetWidth / 2);

    this._renderer.setStyle(overlayPositionBox, 'top', this.buttonBottom + 5 + 'px');
    this._renderer.setStyle(overlayPositionBox, 'left', dropDownleft + 'px');
    this._renderer.setStyle(overlayPositionBox, 'height', '100%');
  }

  private _openMenu() {
    this.menuTrigger.menu['_startAnimation']();
  }
}

这篇关于中心Mat-Menu,中心MatMenu与按钮叠加的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆