Как открыть и закрыть меню угловых матов при наведении - PullRequest
0 голосов
/ 04 декабря 2018

Этот вопрос относится к этой проблеме Github, с mat-menu, которую нельзя переключить с помощью наведения мыши, я в основном пытаюсь заменить основанное на начальной загрузке меню горизонтальной навигации на меню угловых материалов.Единственное, что удерживает меня от репликации меню, основанного на начальной загрузке, - это открытие и закрытие mat-menu при наведении курсора.Как упомянуто в вышеупомянутой проблеме Github, есть некоторые обходные пути для достижения того, чего я хочу, например, использование mouseEnter

(mouseenter)="menuTrigger.openMenu()"

или добавление span в Mat-menuдля того, чтобы связать mat-menu close,

<mat-menu #menu="matMenu" overlapTrigger="false">
  <span (mouseleave)="menuTrigger.closeMenu()">
    <button mat-menu-item>Item 1</button>
    <button mat-menu-item>Item 2</button>
  </span>
</mat-menu>

, но ни одно из решений, по-видимому, не охватывает каждый небольшой сценарий,

например,

Как упоминалось в вышеприведенном выпуске GithubВ первом решении SO есть следующие проблемы:

  • Наведите курсор мыши на кнопку, и появится меню.Но если вы нажмете на кнопку, она скроется и покажет меню.ИМХО, это ошибка.
  • Чтобы скрыть меню, пользователь должен щелкнуть за пределами меню.В идеале меню должно быть скрыто, если курсор мыши находится за пределами
    области (которая включает в себя кнопку, меню и подменю)
    длиннее 400 мс.

И в решении span, которое пытается решить одну из вышеупомянутых проблем, но не работает должным образом, например,

при наведении на MatMenuTrigger открывает mat-menu, как и ожидалось, но если пользователь перемещаетотведите курсор мыши, не вводя mat-menu, затем он не закроется автоматически, что неправильно.

Кроме того, переход к одному из подменю уровней два также закрывает меню первого уровня, что не то, что я хочу,

PS при перемещении мыши из одного открытого меню в следующее одноуровневое не открывается следующее.Я думаю, что это может быть трудно достичь, как упоминалось здесь , но я думаю, что некоторые из них могут быть достижимы, верно?

Вот базовый stackBlitz , который воспроизводит то, что яЯ испытываю любую помощь.

Ответы [ 4 ]

0 голосов
/ 01 августа 2019

Я использую этот способ в своем проекте, но даже я применяю style="z-index:1050 "после того, как я реализую (mouseleave), он не останавливает рекурсивный цикл. Я запутался. Более того, мои компоненты меню являются рекурсивными компонентами меню, я делаюне знаю, имеет ли подменю одинаковое имя триггера, оно будет работать правильно.

0 голосов
/ 10 декабря 2018

Это решение может использоваться в качестве альтернативы настройке z-index: 1050, как предложено маршалом.Для других исправлений вы должны проверить ответ маршала.

Вы можете использовать

<button [matMenuTriggerFor]="menu" #trigger="matMenuTrigger" (mouseenter)="trigger.openMenu()" (mouseleave)="trigger.closeMenu()"></button>

. Это позволит создать непрерывную петлю мерцания, но есть простое исправление.

Только одиноб этом нужно позаботиться, например:

при открытии меню

<div class="cdk-overlay-container"></div>

этот div охватывает весь экран, обычно добавляемый в конце всего html непосредственно перед тегом / body.Все ваши меню создаются внутри этого контейнера.(имя класса может отличаться в разных версиях).

Просто добавьте это в свой файл стилей CSS / SCSS:

.cdk-overlay-container{
    left:200px;
    top:200px;
}
.cdk-overlay-connected-position-bounding-box{
    top:0 !important;

}

или что-нибудь, что мешает этому элементу перекрывать вашу кнопку.

Я попробовал это сам, надеюсь, мой ответ был ясным и точным.

Вот демоверсия stackblitz того же самого, я отредактировал код stackblitz в вопросе.

0 голосов
/ 11 мая 2019

У меня есть POC для клиента, и у него только одно меню верхнего уровня.Я смог заставить это решение работать без z-индекса и рендерера.

Моя триггерная кнопка - это даже не кнопка и не матовая кнопка, это div:

добавил эти атрибуты в div с помощью matMenuTriggerForприписывать.(menuOpened) = "isMatMenuOpen = true;"(menuClosed) = "isMatMenuOpen = false;"

0 голосов
/ 04 декабря 2018

Первая проблема состоит в том, что mat-menu крадет фокус у кнопки, когда генерируется оверлей CDK из-за z-index оверлея ..., чтобы решить это, вам нужно установить z-индекс в стиле длякнопка ...

  • Это остановит рекурсивный цикл при добавлении (mouseleave) к кнопке.style="z-index:1050"

Далее необходимо отследить состояние всех событий входа и выхода для меню levelone и levelTwo и сохранить это состояние в двух компонентных переменных.

enteredButton = false;
isMatMenuOpen = false;
isMatMenu2Open = false;

Затем создайте методы входа в меню и menuLeave для обоих уровней меню. Обратите внимание: menuLeave(trigger) проверяет, доступен ли уровень 2, и ничего не делает, если имеет значение.

Обратите внимание: menu2Leave() имеетлогика, позволяющая вернуться к первому уровню, но закрыть оба при выходе с другой стороны ... также убрать фокус кнопки при выходе из уровней.

menuenter() {
    this.isMatMenuOpen = true;
    if (this.isMatMenu2Open) {
      this.isMatMenu2Open = false;
    }
  }

  menuLeave(trigger, button) {
    setTimeout(() => {
      if (!this.isMatMenu2Open && !this.enteredButton) {
        this.isMatMenuOpen = false;
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.isMatMenuOpen = false;
      }
    }, 80)
  }

  menu2enter() {
    this.isMatMenu2Open = true;
  }

  menu2Leave(trigger1, trigger2, button) {
    setTimeout(() => {
      if (this.isMatMenu2Open) {
        trigger1.closeMenu();
        this.isMatMenuOpen = false;
        this.isMatMenu2Open = false;
        this.enteredButton = false;
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.isMatMenu2Open = false;
        trigger2.closeMenu();
      }
    }, 100)
  }

  buttonEnter(trigger) {
    setTimeout(() => {
      if(this.prevButtonTrigger && this.prevButtonTrigger != trigger){
        this.prevButtonTrigger.closeMenu();
        this.prevButtonTrigger = trigger;
        trigger.openMenu();
      }
      else if (!this.isMatMenuOpen) {
        this.enteredButton = true;
        this.prevButtonTrigger = trigger
        trigger.openMenu()
      }
      else {
        this.enteredButton = true;
        this.prevButtonTrigger = trigger
      }
    })
  }

  buttonLeave(trigger, button) {
    setTimeout(() => {
      if (this.enteredButton && !this.isMatMenuOpen) {
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } if (!this.isMatMenuOpen) {
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.enteredButton = false;
      }
    }, 100)
  }

HTML

ниже показано, как все это подключить.

<ng-container *ngFor="let menuItem of modulesList">

    <ng-container *ngIf="!menuItem.children">
        <a class="nav-link">
            <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span class="text-holder">{{menuItem.label}}</span>
    </a>
  </ng-container>
  <ng-container *ngIf="menuItem.children.length > 0">
    <button #button mat-button [matMenuTriggerFor]="levelOne" #levelOneTrigger="matMenuTrigger" (mouseenter)="levelOneTrigger.openMenu()" (mouseleave)="buttonLeave(levelOneTrigger, button)" style="z-index:1050">
      <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span>{{menuItem.label}}
        <i class="fa fa-chevron-down"></i>
      </span>
    </button>

    <mat-menu #levelOne="matMenu" direction="down" yPosition="below">
      <span (mouseenter)="menuenter()" (mouseleave)="menuLeave(levelOneTrigger, button)">
      <ng-container *ngFor="let childL1 of menuItem.children">
        <li class="p-0" *ngIf="!childL1.children" mat-menu-item>
          <a class="nav-link">{{childL1.label}}
            <i *ngIf="childL1.icon" [ngClass]="childL1.icon"></i>
          </a>
        </li>
        <ng-container *ngIf="childL1.children && childL1.children.length > 0">
          <li mat-menu-item #levelTwoTrigger="matMenuTrigger" [matMenuTriggerFor]="levelTwo">
            <span class="icon fa" [ngClass]="childL1.icon"></span>
            <span>{{childL1.label}}</span>
          </li>

          <mat-menu #levelTwo="matMenu">
            <span (mouseenter)="menu2enter()" (mouseleave)="menu2Leave(levelOneTrigger,levelTwoTrigger, button)">
            <ng-container *ngFor="let childL2 of childL1.children">
              <li class="p-0" mat-menu-item>
                <a class="nav-link">{{childL2.label}}
                  <i *ngIf="childL2.icon" [ngClass]="childL2.icon"></i>
                </a>
              </li>
            </ng-container>
            </span>
          </mat-menu>
        </ng-container>
      </ng-container>
      </span>
    </mat-menu>
  </ng-container>

</ng-container>

Stackblitz

https://stackblitz.com/edit/mat-nested-menu-yclrmd?embed=1&file=app/nested-menu-example.html

...