menu.mjs 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. import * as i0 from '@angular/core';
  2. import { InjectionToken, Component, ChangeDetectionStrategy, ViewEncapsulation, Inject, Optional, Input, Directive, QueryList, EventEmitter, TemplateRef, ContentChildren, ViewChild, ContentChild, Output, inject, ChangeDetectorRef, Self, NgModule } from '@angular/core';
  3. import * as i1 from '@angular/cdk/a11y';
  4. import { FocusKeyManager, isFakeTouchstartFromScreenReader, isFakeMousedownFromScreenReader } from '@angular/cdk/a11y';
  5. import { coerceBooleanProperty } from '@angular/cdk/coercion';
  6. import { UP_ARROW, DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW, ESCAPE, hasModifierKey, ENTER, SPACE } from '@angular/cdk/keycodes';
  7. import { Subject, merge, Subscription, of, asapScheduler } from 'rxjs';
  8. import { startWith, switchMap, take, takeUntil, filter, delay } from 'rxjs/operators';
  9. import * as i3 from '@angular/material/core';
  10. import { mixinDisableRipple, mixinDisabled, MatRippleModule, MatCommonModule } from '@angular/material/core';
  11. import * as i2 from '@angular/common';
  12. import { DOCUMENT, CommonModule } from '@angular/common';
  13. import { TemplatePortal, DomPortalOutlet } from '@angular/cdk/portal';
  14. import { trigger, state, style, transition, animate } from '@angular/animations';
  15. import * as i3$1 from '@angular/cdk/bidi';
  16. import * as i1$1 from '@angular/cdk/overlay';
  17. import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
  18. import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
  19. import { CdkScrollableModule } from '@angular/cdk/scrolling';
  20. /**
  21. * Injection token used to provide the parent menu to menu-specific components.
  22. * @docs-private
  23. */
  24. const MAT_MENU_PANEL = new InjectionToken('MAT_MENU_PANEL');
  25. // Boilerplate for applying mixins to MatMenuItem.
  26. /** @docs-private */
  27. const _MatMenuItemBase = mixinDisableRipple(mixinDisabled(class {
  28. }));
  29. /**
  30. * Single item inside of a `mat-menu`. Provides the menu item styling and accessibility treatment.
  31. */
  32. class MatMenuItem extends _MatMenuItemBase {
  33. constructor(_elementRef, _document, _focusMonitor, _parentMenu, _changeDetectorRef) {
  34. super();
  35. this._elementRef = _elementRef;
  36. this._document = _document;
  37. this._focusMonitor = _focusMonitor;
  38. this._parentMenu = _parentMenu;
  39. this._changeDetectorRef = _changeDetectorRef;
  40. /** ARIA role for the menu item. */
  41. this.role = 'menuitem';
  42. /** Stream that emits when the menu item is hovered. */
  43. this._hovered = new Subject();
  44. /** Stream that emits when the menu item is focused. */
  45. this._focused = new Subject();
  46. /** Whether the menu item is highlighted. */
  47. this._highlighted = false;
  48. /** Whether the menu item acts as a trigger for a sub-menu. */
  49. this._triggersSubmenu = false;
  50. _parentMenu?.addItem?.(this);
  51. }
  52. /** Focuses the menu item. */
  53. focus(origin, options) {
  54. if (this._focusMonitor && origin) {
  55. this._focusMonitor.focusVia(this._getHostElement(), origin, options);
  56. }
  57. else {
  58. this._getHostElement().focus(options);
  59. }
  60. this._focused.next(this);
  61. }
  62. ngAfterViewInit() {
  63. if (this._focusMonitor) {
  64. // Start monitoring the element so it gets the appropriate focused classes. We want
  65. // to show the focus style for menu items only when the focus was not caused by a
  66. // mouse or touch interaction.
  67. this._focusMonitor.monitor(this._elementRef, false);
  68. }
  69. }
  70. ngOnDestroy() {
  71. if (this._focusMonitor) {
  72. this._focusMonitor.stopMonitoring(this._elementRef);
  73. }
  74. if (this._parentMenu && this._parentMenu.removeItem) {
  75. this._parentMenu.removeItem(this);
  76. }
  77. this._hovered.complete();
  78. this._focused.complete();
  79. }
  80. /** Used to set the `tabindex`. */
  81. _getTabIndex() {
  82. return this.disabled ? '-1' : '0';
  83. }
  84. /** Returns the host DOM element. */
  85. _getHostElement() {
  86. return this._elementRef.nativeElement;
  87. }
  88. /** Prevents the default element actions if it is disabled. */
  89. _checkDisabled(event) {
  90. if (this.disabled) {
  91. event.preventDefault();
  92. event.stopPropagation();
  93. }
  94. }
  95. /** Emits to the hover stream. */
  96. _handleMouseEnter() {
  97. this._hovered.next(this);
  98. }
  99. /** Gets the label to be used when determining whether the option should be focused. */
  100. getLabel() {
  101. const clone = this._elementRef.nativeElement.cloneNode(true);
  102. const icons = clone.querySelectorAll('mat-icon, .material-icons');
  103. // Strip away icons so they don't show up in the text.
  104. for (let i = 0; i < icons.length; i++) {
  105. icons[i].remove();
  106. }
  107. return clone.textContent?.trim() || '';
  108. }
  109. _setHighlighted(isHighlighted) {
  110. // We need to mark this for check for the case where the content is coming from a
  111. // `matMenuContent` whose change detection tree is at the declaration position,
  112. // not the insertion position. See #23175.
  113. // @breaking-change 12.0.0 Remove null check for `_changeDetectorRef`.
  114. this._highlighted = isHighlighted;
  115. this._changeDetectorRef?.markForCheck();
  116. }
  117. _setTriggersSubmenu(triggersSubmenu) {
  118. // @breaking-change 12.0.0 Remove null check for `_changeDetectorRef`.
  119. this._triggersSubmenu = triggersSubmenu;
  120. this._changeDetectorRef?.markForCheck();
  121. }
  122. _hasFocus() {
  123. return this._document && this._document.activeElement === this._getHostElement();
  124. }
  125. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuItem, deps: [{ token: i0.ElementRef }, { token: DOCUMENT }, { token: i1.FocusMonitor }, { token: MAT_MENU_PANEL, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
  126. static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: MatMenuItem, selector: "[mat-menu-item]", inputs: { disabled: "disabled", disableRipple: "disableRipple", role: "role" }, host: { listeners: { "click": "_checkDisabled($event)", "mouseenter": "_handleMouseEnter()" }, properties: { "attr.role": "role", "class.mat-mdc-menu-item-highlighted": "_highlighted", "class.mat-mdc-menu-item-submenu-trigger": "_triggersSubmenu", "attr.tabindex": "_getTabIndex()", "attr.aria-disabled": "disabled", "attr.disabled": "disabled || null" }, classAttribute: "mat-mdc-menu-item mat-mdc-focus-indicator mdc-list-item" }, exportAs: ["matMenuItem"], usesInheritance: true, ngImport: i0, template: "<ng-content select=\"mat-icon, [matMenuItemIcon]\"></ng-content>\n<span class=\"mdc-list-item__primary-text\"><ng-content></ng-content></span>\n<div class=\"mat-mdc-menu-ripple\" matRipple\n [matRippleDisabled]=\"disableRipple || disabled\"\n [matRippleTrigger]=\"_getHostElement()\">\n</div>\n<svg\n *ngIf=\"_triggersSubmenu\"\n class=\"mat-mdc-menu-submenu-icon\"\n viewBox=\"0 0 5 10\"\n focusable=\"false\"\n aria-hidden=\"true\"><polygon points=\"0,0 5,5 0,10\"/></svg>\n", dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
  127. }
  128. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuItem, decorators: [{
  129. type: Component,
  130. args: [{ selector: '[mat-menu-item]', exportAs: 'matMenuItem', inputs: ['disabled', 'disableRipple'], host: {
  131. '[attr.role]': 'role',
  132. 'class': 'mat-mdc-menu-item mat-mdc-focus-indicator mdc-list-item',
  133. '[class.mat-mdc-menu-item-highlighted]': '_highlighted',
  134. '[class.mat-mdc-menu-item-submenu-trigger]': '_triggersSubmenu',
  135. '[attr.tabindex]': '_getTabIndex()',
  136. '[attr.aria-disabled]': 'disabled',
  137. '[attr.disabled]': 'disabled || null',
  138. '(click)': '_checkDisabled($event)',
  139. '(mouseenter)': '_handleMouseEnter()',
  140. }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ng-content select=\"mat-icon, [matMenuItemIcon]\"></ng-content>\n<span class=\"mdc-list-item__primary-text\"><ng-content></ng-content></span>\n<div class=\"mat-mdc-menu-ripple\" matRipple\n [matRippleDisabled]=\"disableRipple || disabled\"\n [matRippleTrigger]=\"_getHostElement()\">\n</div>\n<svg\n *ngIf=\"_triggersSubmenu\"\n class=\"mat-mdc-menu-submenu-icon\"\n viewBox=\"0 0 5 10\"\n focusable=\"false\"\n aria-hidden=\"true\"><polygon points=\"0,0 5,5 0,10\"/></svg>\n" }]
  141. }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: undefined, decorators: [{
  142. type: Inject,
  143. args: [DOCUMENT]
  144. }] }, { type: i1.FocusMonitor }, { type: undefined, decorators: [{
  145. type: Inject,
  146. args: [MAT_MENU_PANEL]
  147. }, {
  148. type: Optional
  149. }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { role: [{
  150. type: Input
  151. }] } });
  152. /**
  153. * Throws an exception for the case when menu's x-position value isn't valid.
  154. * In other words, it doesn't match 'before' or 'after'.
  155. * @docs-private
  156. */
  157. function throwMatMenuInvalidPositionX() {
  158. throw Error(`xPosition value must be either 'before' or after'.
  159. Example: <mat-menu xPosition="before" #menu="matMenu"></mat-menu>`);
  160. }
  161. /**
  162. * Throws an exception for the case when menu's y-position value isn't valid.
  163. * In other words, it doesn't match 'above' or 'below'.
  164. * @docs-private
  165. */
  166. function throwMatMenuInvalidPositionY() {
  167. throw Error(`yPosition value must be either 'above' or below'.
  168. Example: <mat-menu yPosition="above" #menu="matMenu"></mat-menu>`);
  169. }
  170. /**
  171. * Throws an exception for the case when a menu is assigned
  172. * to a trigger that is placed inside the same menu.
  173. * @docs-private
  174. */
  175. function throwMatMenuRecursiveError() {
  176. throw Error(`matMenuTriggerFor: menu cannot contain its own trigger. Assign a menu that is ` +
  177. `not a parent of the trigger or move the trigger outside of the menu.`);
  178. }
  179. /**
  180. * Injection token that can be used to reference instances of `MatMenuContent`. It serves
  181. * as alternative token to the actual `MatMenuContent` class which could cause unnecessary
  182. * retention of the class and its directive metadata.
  183. */
  184. const MAT_MENU_CONTENT = new InjectionToken('MatMenuContent');
  185. class _MatMenuContentBase {
  186. constructor(_template, _componentFactoryResolver, _appRef, _injector, _viewContainerRef, _document, _changeDetectorRef) {
  187. this._template = _template;
  188. this._componentFactoryResolver = _componentFactoryResolver;
  189. this._appRef = _appRef;
  190. this._injector = _injector;
  191. this._viewContainerRef = _viewContainerRef;
  192. this._document = _document;
  193. this._changeDetectorRef = _changeDetectorRef;
  194. /** Emits when the menu content has been attached. */
  195. this._attached = new Subject();
  196. }
  197. /**
  198. * Attaches the content with a particular context.
  199. * @docs-private
  200. */
  201. attach(context = {}) {
  202. if (!this._portal) {
  203. this._portal = new TemplatePortal(this._template, this._viewContainerRef);
  204. }
  205. this.detach();
  206. if (!this._outlet) {
  207. this._outlet = new DomPortalOutlet(this._document.createElement('div'), this._componentFactoryResolver, this._appRef, this._injector);
  208. }
  209. const element = this._template.elementRef.nativeElement;
  210. // Because we support opening the same menu from different triggers (which in turn have their
  211. // own `OverlayRef` panel), we have to re-insert the host element every time, otherwise we
  212. // risk it staying attached to a pane that's no longer in the DOM.
  213. element.parentNode.insertBefore(this._outlet.outletElement, element);
  214. // When `MatMenuContent` is used in an `OnPush` component, the insertion of the menu
  215. // content via `createEmbeddedView` does not cause the content to be seen as "dirty"
  216. // by Angular. This causes the `@ContentChildren` for menu items within the menu to
  217. // not be updated by Angular. By explicitly marking for check here, we tell Angular that
  218. // it needs to check for new menu items and update the `@ContentChild` in `MatMenu`.
  219. // @breaking-change 9.0.0 Make change detector ref required
  220. this._changeDetectorRef?.markForCheck();
  221. this._portal.attach(this._outlet, context);
  222. this._attached.next();
  223. }
  224. /**
  225. * Detaches the content.
  226. * @docs-private
  227. */
  228. detach() {
  229. if (this._portal.isAttached) {
  230. this._portal.detach();
  231. }
  232. }
  233. ngOnDestroy() {
  234. if (this._outlet) {
  235. this._outlet.dispose();
  236. }
  237. }
  238. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatMenuContentBase, deps: [{ token: i0.TemplateRef }, { token: i0.ComponentFactoryResolver }, { token: i0.ApplicationRef }, { token: i0.Injector }, { token: i0.ViewContainerRef }, { token: DOCUMENT }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
  239. static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: _MatMenuContentBase, ngImport: i0 }); }
  240. }
  241. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatMenuContentBase, decorators: [{
  242. type: Directive
  243. }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.ComponentFactoryResolver }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
  244. type: Inject,
  245. args: [DOCUMENT]
  246. }] }, { type: i0.ChangeDetectorRef }]; } });
  247. /** Menu content that will be rendered lazily once the menu is opened. */
  248. class MatMenuContent extends _MatMenuContentBase {
  249. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuContent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
  250. static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: MatMenuContent, selector: "ng-template[matMenuContent]", providers: [{ provide: MAT_MENU_CONTENT, useExisting: MatMenuContent }], usesInheritance: true, ngImport: i0 }); }
  251. }
  252. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuContent, decorators: [{
  253. type: Directive,
  254. args: [{
  255. selector: 'ng-template[matMenuContent]',
  256. providers: [{ provide: MAT_MENU_CONTENT, useExisting: MatMenuContent }],
  257. }]
  258. }] });
  259. /**
  260. * Animations used by the mat-menu component.
  261. * Animation duration and timing values are based on:
  262. * https://material.io/guidelines/components/menus.html#menus-usage
  263. * @docs-private
  264. */
  265. const matMenuAnimations = {
  266. /**
  267. * This animation controls the menu panel's entry and exit from the page.
  268. *
  269. * When the menu panel is added to the DOM, it scales in and fades in its border.
  270. *
  271. * When the menu panel is removed from the DOM, it simply fades out after a brief
  272. * delay to display the ripple.
  273. */
  274. transformMenu: trigger('transformMenu', [
  275. state('void', style({
  276. opacity: 0,
  277. transform: 'scale(0.8)',
  278. })),
  279. transition('void => enter', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({
  280. opacity: 1,
  281. transform: 'scale(1)',
  282. }))),
  283. transition('* => void', animate('100ms 25ms linear', style({ opacity: 0 }))),
  284. ]),
  285. /**
  286. * This animation fades in the background color and content of the menu panel
  287. * after its containing element is scaled in.
  288. */
  289. fadeInItems: trigger('fadeInItems', [
  290. // TODO(crisbeto): this is inside the `transformMenu`
  291. // now. Remove next time we do breaking changes.
  292. state('showing', style({ opacity: 1 })),
  293. transition('void => *', [
  294. style({ opacity: 0 }),
  295. animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)'),
  296. ]),
  297. ]),
  298. };
  299. /**
  300. * @deprecated
  301. * @breaking-change 8.0.0
  302. * @docs-private
  303. */
  304. const fadeInItems = matMenuAnimations.fadeInItems;
  305. /**
  306. * @deprecated
  307. * @breaking-change 8.0.0
  308. * @docs-private
  309. */
  310. const transformMenu = matMenuAnimations.transformMenu;
  311. let menuPanelUid = 0;
  312. /** Injection token to be used to override the default options for `mat-menu`. */
  313. const MAT_MENU_DEFAULT_OPTIONS = new InjectionToken('mat-menu-default-options', {
  314. providedIn: 'root',
  315. factory: MAT_MENU_DEFAULT_OPTIONS_FACTORY,
  316. });
  317. /** @docs-private */
  318. function MAT_MENU_DEFAULT_OPTIONS_FACTORY() {
  319. return {
  320. overlapTrigger: false,
  321. xPosition: 'after',
  322. yPosition: 'below',
  323. backdropClass: 'cdk-overlay-transparent-backdrop',
  324. };
  325. }
  326. /** Base class with all of the `MatMenu` functionality. */
  327. class _MatMenuBase {
  328. /** Position of the menu in the X axis. */
  329. get xPosition() {
  330. return this._xPosition;
  331. }
  332. set xPosition(value) {
  333. if (value !== 'before' &&
  334. value !== 'after' &&
  335. (typeof ngDevMode === 'undefined' || ngDevMode)) {
  336. throwMatMenuInvalidPositionX();
  337. }
  338. this._xPosition = value;
  339. this.setPositionClasses();
  340. }
  341. /** Position of the menu in the Y axis. */
  342. get yPosition() {
  343. return this._yPosition;
  344. }
  345. set yPosition(value) {
  346. if (value !== 'above' && value !== 'below' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
  347. throwMatMenuInvalidPositionY();
  348. }
  349. this._yPosition = value;
  350. this.setPositionClasses();
  351. }
  352. /** Whether the menu should overlap its trigger. */
  353. get overlapTrigger() {
  354. return this._overlapTrigger;
  355. }
  356. set overlapTrigger(value) {
  357. this._overlapTrigger = coerceBooleanProperty(value);
  358. }
  359. /** Whether the menu has a backdrop. */
  360. get hasBackdrop() {
  361. return this._hasBackdrop;
  362. }
  363. set hasBackdrop(value) {
  364. this._hasBackdrop = coerceBooleanProperty(value);
  365. }
  366. /**
  367. * This method takes classes set on the host mat-menu element and applies them on the
  368. * menu template that displays in the overlay container. Otherwise, it's difficult
  369. * to style the containing menu from outside the component.
  370. * @param classes list of class names
  371. */
  372. set panelClass(classes) {
  373. const previousPanelClass = this._previousPanelClass;
  374. if (previousPanelClass && previousPanelClass.length) {
  375. previousPanelClass.split(' ').forEach((className) => {
  376. this._classList[className] = false;
  377. });
  378. }
  379. this._previousPanelClass = classes;
  380. if (classes && classes.length) {
  381. classes.split(' ').forEach((className) => {
  382. this._classList[className] = true;
  383. });
  384. this._elementRef.nativeElement.className = '';
  385. }
  386. }
  387. /**
  388. * This method takes classes set on the host mat-menu element and applies them on the
  389. * menu template that displays in the overlay container. Otherwise, it's difficult
  390. * to style the containing menu from outside the component.
  391. * @deprecated Use `panelClass` instead.
  392. * @breaking-change 8.0.0
  393. */
  394. get classList() {
  395. return this.panelClass;
  396. }
  397. set classList(classes) {
  398. this.panelClass = classes;
  399. }
  400. constructor(_elementRef, _ngZone, defaultOptions,
  401. // @breaking-change 15.0.0 `_changeDetectorRef` to become a required parameter.
  402. _changeDetectorRef) {
  403. this._elementRef = _elementRef;
  404. this._ngZone = _ngZone;
  405. this._changeDetectorRef = _changeDetectorRef;
  406. /** Only the direct descendant menu items. */
  407. this._directDescendantItems = new QueryList();
  408. /** Config object to be passed into the menu's ngClass */
  409. this._classList = {};
  410. /** Current state of the panel animation. */
  411. this._panelAnimationState = 'void';
  412. /** Emits whenever an animation on the menu completes. */
  413. this._animationDone = new Subject();
  414. /** Event emitted when the menu is closed. */
  415. this.closed = new EventEmitter();
  416. /**
  417. * Event emitted when the menu is closed.
  418. * @deprecated Switch to `closed` instead
  419. * @breaking-change 8.0.0
  420. */
  421. this.close = this.closed;
  422. this.panelId = `mat-menu-panel-${menuPanelUid++}`;
  423. this.overlayPanelClass = defaultOptions.overlayPanelClass || '';
  424. this._xPosition = defaultOptions.xPosition;
  425. this._yPosition = defaultOptions.yPosition;
  426. this.backdropClass = defaultOptions.backdropClass;
  427. this._overlapTrigger = defaultOptions.overlapTrigger;
  428. this._hasBackdrop = defaultOptions.hasBackdrop;
  429. }
  430. ngOnInit() {
  431. this.setPositionClasses();
  432. }
  433. ngAfterContentInit() {
  434. this._updateDirectDescendants();
  435. this._keyManager = new FocusKeyManager(this._directDescendantItems)
  436. .withWrap()
  437. .withTypeAhead()
  438. .withHomeAndEnd();
  439. this._keyManager.tabOut.subscribe(() => this.closed.emit('tab'));
  440. // If a user manually (programmatically) focuses a menu item, we need to reflect that focus
  441. // change back to the key manager. Note that we don't need to unsubscribe here because _focused
  442. // is internal and we know that it gets completed on destroy.
  443. this._directDescendantItems.changes
  444. .pipe(startWith(this._directDescendantItems), switchMap(items => merge(...items.map((item) => item._focused))))
  445. .subscribe(focusedItem => this._keyManager.updateActiveItem(focusedItem));
  446. this._directDescendantItems.changes.subscribe((itemsList) => {
  447. // Move focus to another item, if the active item is removed from the list.
  448. // We need to debounce the callback, because multiple items might be removed
  449. // in quick succession.
  450. const manager = this._keyManager;
  451. if (this._panelAnimationState === 'enter' && manager.activeItem?._hasFocus()) {
  452. const items = itemsList.toArray();
  453. const index = Math.max(0, Math.min(items.length - 1, manager.activeItemIndex || 0));
  454. if (items[index] && !items[index].disabled) {
  455. manager.setActiveItem(index);
  456. }
  457. else {
  458. manager.setNextItemActive();
  459. }
  460. }
  461. });
  462. }
  463. ngOnDestroy() {
  464. this._keyManager?.destroy();
  465. this._directDescendantItems.destroy();
  466. this.closed.complete();
  467. this._firstItemFocusSubscription?.unsubscribe();
  468. }
  469. /** Stream that emits whenever the hovered menu item changes. */
  470. _hovered() {
  471. // Coerce the `changes` property because Angular types it as `Observable<any>`
  472. const itemChanges = this._directDescendantItems.changes;
  473. return itemChanges.pipe(startWith(this._directDescendantItems), switchMap(items => merge(...items.map((item) => item._hovered))));
  474. }
  475. /*
  476. * Registers a menu item with the menu.
  477. * @docs-private
  478. * @deprecated No longer being used. To be removed.
  479. * @breaking-change 9.0.0
  480. */
  481. addItem(_item) { }
  482. /**
  483. * Removes an item from the menu.
  484. * @docs-private
  485. * @deprecated No longer being used. To be removed.
  486. * @breaking-change 9.0.0
  487. */
  488. removeItem(_item) { }
  489. /** Handle a keyboard event from the menu, delegating to the appropriate action. */
  490. _handleKeydown(event) {
  491. const keyCode = event.keyCode;
  492. const manager = this._keyManager;
  493. switch (keyCode) {
  494. case ESCAPE:
  495. if (!hasModifierKey(event)) {
  496. event.preventDefault();
  497. this.closed.emit('keydown');
  498. }
  499. break;
  500. case LEFT_ARROW:
  501. if (this.parentMenu && this.direction === 'ltr') {
  502. this.closed.emit('keydown');
  503. }
  504. break;
  505. case RIGHT_ARROW:
  506. if (this.parentMenu && this.direction === 'rtl') {
  507. this.closed.emit('keydown');
  508. }
  509. break;
  510. default:
  511. if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) {
  512. manager.setFocusOrigin('keyboard');
  513. }
  514. manager.onKeydown(event);
  515. return;
  516. }
  517. // Don't allow the event to propagate if we've already handled it, or it may
  518. // end up reaching other overlays that were opened earlier (see #22694).
  519. event.stopPropagation();
  520. }
  521. /**
  522. * Focus the first item in the menu.
  523. * @param origin Action from which the focus originated. Used to set the correct styling.
  524. */
  525. focusFirstItem(origin = 'program') {
  526. // Wait for `onStable` to ensure iOS VoiceOver screen reader focuses the first item (#24735).
  527. this._firstItemFocusSubscription?.unsubscribe();
  528. this._firstItemFocusSubscription = this._ngZone.onStable.pipe(take(1)).subscribe(() => {
  529. let menuPanel = null;
  530. if (this._directDescendantItems.length) {
  531. // Because the `mat-menuPanel` is at the DOM insertion point, not inside the overlay, we don't
  532. // have a nice way of getting a hold of the menuPanel panel. We can't use a `ViewChild` either
  533. // because the panel is inside an `ng-template`. We work around it by starting from one of
  534. // the items and walking up the DOM.
  535. menuPanel = this._directDescendantItems.first._getHostElement().closest('[role="menu"]');
  536. }
  537. // If an item in the menuPanel is already focused, avoid overriding the focus.
  538. if (!menuPanel || !menuPanel.contains(document.activeElement)) {
  539. const manager = this._keyManager;
  540. manager.setFocusOrigin(origin).setFirstItemActive();
  541. // If there's no active item at this point, it means that all the items are disabled.
  542. // Move focus to the menuPanel panel so keyboard events like Escape still work. Also this will
  543. // give _some_ feedback to screen readers.
  544. if (!manager.activeItem && menuPanel) {
  545. menuPanel.focus();
  546. }
  547. }
  548. });
  549. }
  550. /**
  551. * Resets the active item in the menu. This is used when the menu is opened, allowing
  552. * the user to start from the first option when pressing the down arrow.
  553. */
  554. resetActiveItem() {
  555. this._keyManager.setActiveItem(-1);
  556. }
  557. /**
  558. * Sets the menu panel elevation.
  559. * @param depth Number of parent menus that come before the menu.
  560. */
  561. setElevation(depth) {
  562. // The elevation starts at the base and increases by one for each level.
  563. // Capped at 24 because that's the maximum elevation defined in the Material design spec.
  564. const elevation = Math.min(this._baseElevation + depth, 24);
  565. const newElevation = `${this._elevationPrefix}${elevation}`;
  566. const customElevation = Object.keys(this._classList).find(className => {
  567. return className.startsWith(this._elevationPrefix);
  568. });
  569. if (!customElevation || customElevation === this._previousElevation) {
  570. if (this._previousElevation) {
  571. this._classList[this._previousElevation] = false;
  572. }
  573. this._classList[newElevation] = true;
  574. this._previousElevation = newElevation;
  575. }
  576. }
  577. /**
  578. * Adds classes to the menu panel based on its position. Can be used by
  579. * consumers to add specific styling based on the position.
  580. * @param posX Position of the menu along the x axis.
  581. * @param posY Position of the menu along the y axis.
  582. * @docs-private
  583. */
  584. setPositionClasses(posX = this.xPosition, posY = this.yPosition) {
  585. const classes = this._classList;
  586. classes['mat-menu-before'] = posX === 'before';
  587. classes['mat-menu-after'] = posX === 'after';
  588. classes['mat-menu-above'] = posY === 'above';
  589. classes['mat-menu-below'] = posY === 'below';
  590. // @breaking-change 15.0.0 Remove null check for `_changeDetectorRef`.
  591. this._changeDetectorRef?.markForCheck();
  592. }
  593. /** Starts the enter animation. */
  594. _startAnimation() {
  595. // @breaking-change 8.0.0 Combine with _resetAnimation.
  596. this._panelAnimationState = 'enter';
  597. }
  598. /** Resets the panel animation to its initial state. */
  599. _resetAnimation() {
  600. // @breaking-change 8.0.0 Combine with _startAnimation.
  601. this._panelAnimationState = 'void';
  602. }
  603. /** Callback that is invoked when the panel animation completes. */
  604. _onAnimationDone(event) {
  605. this._animationDone.next(event);
  606. this._isAnimating = false;
  607. }
  608. _onAnimationStart(event) {
  609. this._isAnimating = true;
  610. // Scroll the content element to the top as soon as the animation starts. This is necessary,
  611. // because we move focus to the first item while it's still being animated, which can throw
  612. // the browser off when it determines the scroll position. Alternatively we can move focus
  613. // when the animation is done, however moving focus asynchronously will interrupt screen
  614. // readers which are in the process of reading out the menu already. We take the `element`
  615. // from the `event` since we can't use a `ViewChild` to access the pane.
  616. if (event.toState === 'enter' && this._keyManager.activeItemIndex === 0) {
  617. event.element.scrollTop = 0;
  618. }
  619. }
  620. /**
  621. * Sets up a stream that will keep track of any newly-added menu items and will update the list
  622. * of direct descendants. We collect the descendants this way, because `_allItems` can include
  623. * items that are part of child menus, and using a custom way of registering items is unreliable
  624. * when it comes to maintaining the item order.
  625. */
  626. _updateDirectDescendants() {
  627. this._allItems.changes
  628. .pipe(startWith(this._allItems))
  629. .subscribe((items) => {
  630. this._directDescendantItems.reset(items.filter(item => item._parentMenu === this));
  631. this._directDescendantItems.notifyOnChanges();
  632. });
  633. }
  634. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatMenuBase, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: MAT_MENU_DEFAULT_OPTIONS }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
  635. static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: _MatMenuBase, inputs: { backdropClass: "backdropClass", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], ariaDescribedby: ["aria-describedby", "ariaDescribedby"], xPosition: "xPosition", yPosition: "yPosition", overlapTrigger: "overlapTrigger", hasBackdrop: "hasBackdrop", panelClass: ["class", "panelClass"], classList: "classList" }, outputs: { closed: "closed", close: "close" }, queries: [{ propertyName: "lazyContent", first: true, predicate: MAT_MENU_CONTENT, descendants: true }, { propertyName: "_allItems", predicate: MatMenuItem, descendants: true }, { propertyName: "items", predicate: MatMenuItem }], viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0 }); }
  636. }
  637. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatMenuBase, decorators: [{
  638. type: Directive
  639. }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
  640. type: Inject,
  641. args: [MAT_MENU_DEFAULT_OPTIONS]
  642. }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { _allItems: [{
  643. type: ContentChildren,
  644. args: [MatMenuItem, { descendants: true }]
  645. }], backdropClass: [{
  646. type: Input
  647. }], ariaLabel: [{
  648. type: Input,
  649. args: ['aria-label']
  650. }], ariaLabelledby: [{
  651. type: Input,
  652. args: ['aria-labelledby']
  653. }], ariaDescribedby: [{
  654. type: Input,
  655. args: ['aria-describedby']
  656. }], xPosition: [{
  657. type: Input
  658. }], yPosition: [{
  659. type: Input
  660. }], templateRef: [{
  661. type: ViewChild,
  662. args: [TemplateRef]
  663. }], items: [{
  664. type: ContentChildren,
  665. args: [MatMenuItem, { descendants: false }]
  666. }], lazyContent: [{
  667. type: ContentChild,
  668. args: [MAT_MENU_CONTENT]
  669. }], overlapTrigger: [{
  670. type: Input
  671. }], hasBackdrop: [{
  672. type: Input
  673. }], panelClass: [{
  674. type: Input,
  675. args: ['class']
  676. }], classList: [{
  677. type: Input
  678. }], closed: [{
  679. type: Output
  680. }], close: [{
  681. type: Output
  682. }] } });
  683. class MatMenu extends _MatMenuBase {
  684. constructor(_elementRef, _ngZone, _defaultOptions, changeDetectorRef) {
  685. super(_elementRef, _ngZone, _defaultOptions, changeDetectorRef);
  686. this._elevationPrefix = 'mat-elevation-z';
  687. this._baseElevation = 8;
  688. }
  689. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenu, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: MAT_MENU_DEFAULT_OPTIONS }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
  690. static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: MatMenu, selector: "mat-menu", host: { attributes: { "ngSkipHydration": "" }, properties: { "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.aria-describedby": "null" } }, providers: [{ provide: MAT_MENU_PANEL, useExisting: MatMenu }], exportAs: ["matMenu"], usesInheritance: true, ngImport: i0, template: "<ng-template>\n <div\n class=\"mat-mdc-menu-panel mdc-menu-surface mdc-menu-surface--open mat-mdc-elevation-specific\"\n [id]=\"panelId\"\n [ngClass]=\"_classList\"\n (keydown)=\"_handleKeydown($event)\"\n (click)=\"closed.emit('click')\"\n [@transformMenu]=\"_panelAnimationState\"\n (@transformMenu.start)=\"_onAnimationStart($event)\"\n (@transformMenu.done)=\"_onAnimationDone($event)\"\n tabindex=\"-1\"\n role=\"menu\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledby || null\"\n [attr.aria-describedby]=\"ariaDescribedby || null\">\n <div class=\"mat-mdc-menu-content mdc-list\">\n <ng-content></ng-content>\n </div>\n </div>\n</ng-template>\n", styles: [".mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;margin:0;padding:0;transform:scale(1);transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--animating-open{display:inline-block;transform:scale(0.8);opacity:0}.mdc-menu-surface--open{display:inline-block;transform:scale(1);opacity:1}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0}[dir=rtl] .mdc-menu-surface,.mdc-menu-surface[dir=rtl]{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed}.mdc-menu-surface--fullwidth{width:100%}.mdc-menu-surface{max-width:var(--mdc-menu-max-width, calc(100vw - 32px));max-height:var(--mdc-menu-max-height, calc(100vh - 32px));z-index:8;border-radius:var(--mdc-shape-medium, 4px)}mat-menu{display:none}.mat-mdc-menu-content{margin:0;padding:8px 0;list-style-type:none}.mat-mdc-menu-content:focus{outline:none}.mat-mdc-menu-panel.ng-animating{pointer-events:none}.cdk-high-contrast-active .mat-mdc-menu-panel{outline:solid 1px}.mat-mdc-menu-panel.mat-mdc-menu-panel{min-width:112px;max-width:280px;overflow:auto;-webkit-overflow-scrolling:touch;position:relative}.mat-mdc-menu-item{display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;padding:0;padding-left:16px;padding-right:16px;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);cursor:pointer;width:100%;text-align:left;box-sizing:border-box;color:inherit;font-size:inherit;background:none;text-decoration:none;margin:0;min-height:48px}.mat-mdc-menu-item:focus{outline:none}[dir=rtl] .mat-mdc-menu-item,.mat-mdc-menu-item[dir=rtl]{padding-left:16px;padding-right:16px}.mat-mdc-menu-item::-moz-focus-inner{border:0}.mat-mdc-menu-item.mdc-list-item{align-items:center}.mat-mdc-menu-item[disabled]{cursor:default;opacity:.38}.mat-mdc-menu-item[disabled]::after{display:block;position:absolute;content:\"\";top:0;left:0;bottom:0;right:0}.mat-mdc-menu-item .mat-icon{margin-right:16px}[dir=rtl] .mat-mdc-menu-item{text-align:right}[dir=rtl] .mat-mdc-menu-item .mat-icon{margin-right:0;margin-left:16px}.mat-mdc-menu-item .mdc-list-item__primary-text{white-space:normal}.mat-mdc-menu-item.mat-mdc-menu-item-submenu-trigger{padding-right:32px}[dir=rtl] .mat-mdc-menu-item.mat-mdc-menu-item-submenu-trigger{padding-right:16px;padding-left:32px}.cdk-high-contrast-active .mat-mdc-menu-item{margin-top:1px}.mat-mdc-menu-submenu-icon{position:absolute;top:50%;right:16px;transform:translateY(-50%);width:5px;height:10px;fill:currentColor}[dir=rtl] .mat-mdc-menu-submenu-icon{right:auto;left:16px;transform:translateY(-50%) scaleX(-1)}.cdk-high-contrast-active .mat-mdc-menu-submenu-icon{fill:CanvasText}.mat-mdc-menu-item .mat-mdc-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], animations: [matMenuAnimations.transformMenu, matMenuAnimations.fadeInItems], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
  691. }
  692. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenu, decorators: [{
  693. type: Component,
  694. args: [{ selector: 'mat-menu', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, exportAs: 'matMenu', host: {
  695. '[attr.aria-label]': 'null',
  696. '[attr.aria-labelledby]': 'null',
  697. '[attr.aria-describedby]': 'null',
  698. 'ngSkipHydration': '',
  699. }, animations: [matMenuAnimations.transformMenu, matMenuAnimations.fadeInItems], providers: [{ provide: MAT_MENU_PANEL, useExisting: MatMenu }], template: "<ng-template>\n <div\n class=\"mat-mdc-menu-panel mdc-menu-surface mdc-menu-surface--open mat-mdc-elevation-specific\"\n [id]=\"panelId\"\n [ngClass]=\"_classList\"\n (keydown)=\"_handleKeydown($event)\"\n (click)=\"closed.emit('click')\"\n [@transformMenu]=\"_panelAnimationState\"\n (@transformMenu.start)=\"_onAnimationStart($event)\"\n (@transformMenu.done)=\"_onAnimationDone($event)\"\n tabindex=\"-1\"\n role=\"menu\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledby || null\"\n [attr.aria-describedby]=\"ariaDescribedby || null\">\n <div class=\"mat-mdc-menu-content mdc-list\">\n <ng-content></ng-content>\n </div>\n </div>\n</ng-template>\n", styles: [".mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;margin:0;padding:0;transform:scale(1);transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--animating-open{display:inline-block;transform:scale(0.8);opacity:0}.mdc-menu-surface--open{display:inline-block;transform:scale(1);opacity:1}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0}[dir=rtl] .mdc-menu-surface,.mdc-menu-surface[dir=rtl]{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed}.mdc-menu-surface--fullwidth{width:100%}.mdc-menu-surface{max-width:var(--mdc-menu-max-width, calc(100vw - 32px));max-height:var(--mdc-menu-max-height, calc(100vh - 32px));z-index:8;border-radius:var(--mdc-shape-medium, 4px)}mat-menu{display:none}.mat-mdc-menu-content{margin:0;padding:8px 0;list-style-type:none}.mat-mdc-menu-content:focus{outline:none}.mat-mdc-menu-panel.ng-animating{pointer-events:none}.cdk-high-contrast-active .mat-mdc-menu-panel{outline:solid 1px}.mat-mdc-menu-panel.mat-mdc-menu-panel{min-width:112px;max-width:280px;overflow:auto;-webkit-overflow-scrolling:touch;position:relative}.mat-mdc-menu-item{display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;padding:0;padding-left:16px;padding-right:16px;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);cursor:pointer;width:100%;text-align:left;box-sizing:border-box;color:inherit;font-size:inherit;background:none;text-decoration:none;margin:0;min-height:48px}.mat-mdc-menu-item:focus{outline:none}[dir=rtl] .mat-mdc-menu-item,.mat-mdc-menu-item[dir=rtl]{padding-left:16px;padding-right:16px}.mat-mdc-menu-item::-moz-focus-inner{border:0}.mat-mdc-menu-item.mdc-list-item{align-items:center}.mat-mdc-menu-item[disabled]{cursor:default;opacity:.38}.mat-mdc-menu-item[disabled]::after{display:block;position:absolute;content:\"\";top:0;left:0;bottom:0;right:0}.mat-mdc-menu-item .mat-icon{margin-right:16px}[dir=rtl] .mat-mdc-menu-item{text-align:right}[dir=rtl] .mat-mdc-menu-item .mat-icon{margin-right:0;margin-left:16px}.mat-mdc-menu-item .mdc-list-item__primary-text{white-space:normal}.mat-mdc-menu-item.mat-mdc-menu-item-submenu-trigger{padding-right:32px}[dir=rtl] .mat-mdc-menu-item.mat-mdc-menu-item-submenu-trigger{padding-right:16px;padding-left:32px}.cdk-high-contrast-active .mat-mdc-menu-item{margin-top:1px}.mat-mdc-menu-submenu-icon{position:absolute;top:50%;right:16px;transform:translateY(-50%);width:5px;height:10px;fill:currentColor}[dir=rtl] .mat-mdc-menu-submenu-icon{right:auto;left:16px;transform:translateY(-50%) scaleX(-1)}.cdk-high-contrast-active .mat-mdc-menu-submenu-icon{fill:CanvasText}.mat-mdc-menu-item .mat-mdc-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}"] }]
  700. }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
  701. type: Inject,
  702. args: [MAT_MENU_DEFAULT_OPTIONS]
  703. }] }, { type: i0.ChangeDetectorRef }]; } });
  704. /** Injection token that determines the scroll handling while the menu is open. */
  705. const MAT_MENU_SCROLL_STRATEGY = new InjectionToken('mat-menu-scroll-strategy');
  706. /** @docs-private */
  707. function MAT_MENU_SCROLL_STRATEGY_FACTORY(overlay) {
  708. return () => overlay.scrollStrategies.reposition();
  709. }
  710. /** @docs-private */
  711. const MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER = {
  712. provide: MAT_MENU_SCROLL_STRATEGY,
  713. deps: [Overlay],
  714. useFactory: MAT_MENU_SCROLL_STRATEGY_FACTORY,
  715. };
  716. /** Options for binding a passive event listener. */
  717. const passiveEventListenerOptions = normalizePassiveListenerOptions({ passive: true });
  718. /**
  719. * Default top padding of the menu panel.
  720. * @deprecated No longer being used. Will be removed.
  721. * @breaking-change 15.0.0
  722. */
  723. const MENU_PANEL_TOP_PADDING = 8;
  724. class _MatMenuTriggerBase {
  725. /**
  726. * @deprecated
  727. * @breaking-change 8.0.0
  728. */
  729. get _deprecatedMatMenuTriggerFor() {
  730. return this.menu;
  731. }
  732. set _deprecatedMatMenuTriggerFor(v) {
  733. this.menu = v;
  734. }
  735. /** References the menu instance that the trigger is associated with. */
  736. get menu() {
  737. return this._menu;
  738. }
  739. set menu(menu) {
  740. if (menu === this._menu) {
  741. return;
  742. }
  743. this._menu = menu;
  744. this._menuCloseSubscription.unsubscribe();
  745. if (menu) {
  746. if (menu === this._parentMaterialMenu && (typeof ngDevMode === 'undefined' || ngDevMode)) {
  747. throwMatMenuRecursiveError();
  748. }
  749. this._menuCloseSubscription = menu.close.subscribe((reason) => {
  750. this._destroyMenu(reason);
  751. // If a click closed the menu, we should close the entire chain of nested menus.
  752. if ((reason === 'click' || reason === 'tab') && this._parentMaterialMenu) {
  753. this._parentMaterialMenu.closed.emit(reason);
  754. }
  755. });
  756. }
  757. this._menuItemInstance?._setTriggersSubmenu(this.triggersSubmenu());
  758. }
  759. constructor(_overlay, _element, _viewContainerRef, scrollStrategy, parentMenu,
  760. // `MatMenuTrigger` is commonly used in combination with a `MatMenuItem`.
  761. // tslint:disable-next-line: lightweight-tokens
  762. _menuItemInstance, _dir, _focusMonitor, _ngZone) {
  763. this._overlay = _overlay;
  764. this._element = _element;
  765. this._viewContainerRef = _viewContainerRef;
  766. this._menuItemInstance = _menuItemInstance;
  767. this._dir = _dir;
  768. this._focusMonitor = _focusMonitor;
  769. this._ngZone = _ngZone;
  770. this._overlayRef = null;
  771. this._menuOpen = false;
  772. this._closingActionsSubscription = Subscription.EMPTY;
  773. this._hoverSubscription = Subscription.EMPTY;
  774. this._menuCloseSubscription = Subscription.EMPTY;
  775. this._changeDetectorRef = inject(ChangeDetectorRef);
  776. /**
  777. * Handles touch start events on the trigger.
  778. * Needs to be an arrow function so we can easily use addEventListener and removeEventListener.
  779. */
  780. this._handleTouchStart = (event) => {
  781. if (!isFakeTouchstartFromScreenReader(event)) {
  782. this._openedBy = 'touch';
  783. }
  784. };
  785. // Tracking input type is necessary so it's possible to only auto-focus
  786. // the first item of the list when the menu is opened via the keyboard
  787. this._openedBy = undefined;
  788. /**
  789. * Whether focus should be restored when the menu is closed.
  790. * Note that disabling this option can have accessibility implications
  791. * and it's up to you to manage focus, if you decide to turn it off.
  792. */
  793. this.restoreFocus = true;
  794. /** Event emitted when the associated menu is opened. */
  795. this.menuOpened = new EventEmitter();
  796. /**
  797. * Event emitted when the associated menu is opened.
  798. * @deprecated Switch to `menuOpened` instead
  799. * @breaking-change 8.0.0
  800. */
  801. // tslint:disable-next-line:no-output-on-prefix
  802. this.onMenuOpen = this.menuOpened;
  803. /** Event emitted when the associated menu is closed. */
  804. this.menuClosed = new EventEmitter();
  805. /**
  806. * Event emitted when the associated menu is closed.
  807. * @deprecated Switch to `menuClosed` instead
  808. * @breaking-change 8.0.0
  809. */
  810. // tslint:disable-next-line:no-output-on-prefix
  811. this.onMenuClose = this.menuClosed;
  812. this._scrollStrategy = scrollStrategy;
  813. this._parentMaterialMenu = parentMenu instanceof _MatMenuBase ? parentMenu : undefined;
  814. _element.nativeElement.addEventListener('touchstart', this._handleTouchStart, passiveEventListenerOptions);
  815. }
  816. ngAfterContentInit() {
  817. this._handleHover();
  818. }
  819. ngOnDestroy() {
  820. if (this._overlayRef) {
  821. this._overlayRef.dispose();
  822. this._overlayRef = null;
  823. }
  824. this._element.nativeElement.removeEventListener('touchstart', this._handleTouchStart, passiveEventListenerOptions);
  825. this._menuCloseSubscription.unsubscribe();
  826. this._closingActionsSubscription.unsubscribe();
  827. this._hoverSubscription.unsubscribe();
  828. }
  829. /** Whether the menu is open. */
  830. get menuOpen() {
  831. return this._menuOpen;
  832. }
  833. /** The text direction of the containing app. */
  834. get dir() {
  835. return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
  836. }
  837. /** Whether the menu triggers a sub-menu or a top-level one. */
  838. triggersSubmenu() {
  839. return !!(this._menuItemInstance && this._parentMaterialMenu && this.menu);
  840. }
  841. /** Toggles the menu between the open and closed states. */
  842. toggleMenu() {
  843. return this._menuOpen ? this.closeMenu() : this.openMenu();
  844. }
  845. /** Opens the menu. */
  846. openMenu() {
  847. const menu = this.menu;
  848. if (this._menuOpen || !menu) {
  849. return;
  850. }
  851. const overlayRef = this._createOverlay(menu);
  852. const overlayConfig = overlayRef.getConfig();
  853. const positionStrategy = overlayConfig.positionStrategy;
  854. this._setPosition(menu, positionStrategy);
  855. overlayConfig.hasBackdrop =
  856. menu.hasBackdrop == null ? !this.triggersSubmenu() : menu.hasBackdrop;
  857. overlayRef.attach(this._getPortal(menu));
  858. if (menu.lazyContent) {
  859. menu.lazyContent.attach(this.menuData);
  860. }
  861. this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this.closeMenu());
  862. this._initMenu(menu);
  863. if (menu instanceof _MatMenuBase) {
  864. menu._startAnimation();
  865. menu._directDescendantItems.changes.pipe(takeUntil(menu.close)).subscribe(() => {
  866. // Re-adjust the position without locking when the amount of items
  867. // changes so that the overlay is allowed to pick a new optimal position.
  868. positionStrategy.withLockedPosition(false).reapplyLastPosition();
  869. positionStrategy.withLockedPosition(true);
  870. });
  871. }
  872. }
  873. /** Closes the menu. */
  874. closeMenu() {
  875. this.menu?.close.emit();
  876. }
  877. /**
  878. * Focuses the menu trigger.
  879. * @param origin Source of the menu trigger's focus.
  880. */
  881. focus(origin, options) {
  882. if (this._focusMonitor && origin) {
  883. this._focusMonitor.focusVia(this._element, origin, options);
  884. }
  885. else {
  886. this._element.nativeElement.focus(options);
  887. }
  888. }
  889. /**
  890. * Updates the position of the menu to ensure that it fits all options within the viewport.
  891. */
  892. updatePosition() {
  893. this._overlayRef?.updatePosition();
  894. }
  895. /** Closes the menu and does the necessary cleanup. */
  896. _destroyMenu(reason) {
  897. if (!this._overlayRef || !this.menuOpen) {
  898. return;
  899. }
  900. const menu = this.menu;
  901. this._closingActionsSubscription.unsubscribe();
  902. this._overlayRef.detach();
  903. // Always restore focus if the user is navigating using the keyboard or the menu was opened
  904. // programmatically. We don't restore for non-root triggers, because it can prevent focus
  905. // from making it back to the root trigger when closing a long chain of menus by clicking
  906. // on the backdrop.
  907. if (this.restoreFocus && (reason === 'keydown' || !this._openedBy || !this.triggersSubmenu())) {
  908. this.focus(this._openedBy);
  909. }
  910. this._openedBy = undefined;
  911. if (menu instanceof _MatMenuBase) {
  912. menu._resetAnimation();
  913. if (menu.lazyContent) {
  914. // Wait for the exit animation to finish before detaching the content.
  915. menu._animationDone
  916. .pipe(filter(event => event.toState === 'void'), take(1),
  917. // Interrupt if the content got re-attached.
  918. takeUntil(menu.lazyContent._attached))
  919. .subscribe({
  920. next: () => menu.lazyContent.detach(),
  921. // No matter whether the content got re-attached, reset the menu.
  922. complete: () => this._setIsMenuOpen(false),
  923. });
  924. }
  925. else {
  926. this._setIsMenuOpen(false);
  927. }
  928. }
  929. else {
  930. this._setIsMenuOpen(false);
  931. menu?.lazyContent?.detach();
  932. }
  933. }
  934. /**
  935. * This method sets the menu state to open and focuses the first item if
  936. * the menu was opened via the keyboard.
  937. */
  938. _initMenu(menu) {
  939. menu.parentMenu = this.triggersSubmenu() ? this._parentMaterialMenu : undefined;
  940. menu.direction = this.dir;
  941. this._setMenuElevation(menu);
  942. menu.focusFirstItem(this._openedBy || 'program');
  943. this._setIsMenuOpen(true);
  944. }
  945. /** Updates the menu elevation based on the amount of parent menus that it has. */
  946. _setMenuElevation(menu) {
  947. if (menu.setElevation) {
  948. let depth = 0;
  949. let parentMenu = menu.parentMenu;
  950. while (parentMenu) {
  951. depth++;
  952. parentMenu = parentMenu.parentMenu;
  953. }
  954. menu.setElevation(depth);
  955. }
  956. }
  957. // set state rather than toggle to support triggers sharing a menu
  958. _setIsMenuOpen(isOpen) {
  959. if (isOpen !== this._menuOpen) {
  960. this._menuOpen = isOpen;
  961. this._menuOpen ? this.menuOpened.emit() : this.menuClosed.emit();
  962. if (this.triggersSubmenu()) {
  963. this._menuItemInstance._setHighlighted(isOpen);
  964. }
  965. this._changeDetectorRef.markForCheck();
  966. }
  967. }
  968. /**
  969. * This method creates the overlay from the provided menu's template and saves its
  970. * OverlayRef so that it can be attached to the DOM when openMenu is called.
  971. */
  972. _createOverlay(menu) {
  973. if (!this._overlayRef) {
  974. const config = this._getOverlayConfig(menu);
  975. this._subscribeToPositions(menu, config.positionStrategy);
  976. this._overlayRef = this._overlay.create(config);
  977. // Consume the `keydownEvents` in order to prevent them from going to another overlay.
  978. // Ideally we'd also have our keyboard event logic in here, however doing so will
  979. // break anybody that may have implemented the `MatMenuPanel` themselves.
  980. this._overlayRef.keydownEvents().subscribe();
  981. }
  982. return this._overlayRef;
  983. }
  984. /**
  985. * This method builds the configuration object needed to create the overlay, the OverlayState.
  986. * @returns OverlayConfig
  987. */
  988. _getOverlayConfig(menu) {
  989. return new OverlayConfig({
  990. positionStrategy: this._overlay
  991. .position()
  992. .flexibleConnectedTo(this._element)
  993. .withLockedPosition()
  994. .withGrowAfterOpen()
  995. .withTransformOriginOn('.mat-menu-panel, .mat-mdc-menu-panel'),
  996. backdropClass: menu.backdropClass || 'cdk-overlay-transparent-backdrop',
  997. panelClass: menu.overlayPanelClass,
  998. scrollStrategy: this._scrollStrategy(),
  999. direction: this._dir,
  1000. });
  1001. }
  1002. /**
  1003. * Listens to changes in the position of the overlay and sets the correct classes
  1004. * on the menu based on the new position. This ensures the animation origin is always
  1005. * correct, even if a fallback position is used for the overlay.
  1006. */
  1007. _subscribeToPositions(menu, position) {
  1008. if (menu.setPositionClasses) {
  1009. position.positionChanges.subscribe(change => {
  1010. const posX = change.connectionPair.overlayX === 'start' ? 'after' : 'before';
  1011. const posY = change.connectionPair.overlayY === 'top' ? 'below' : 'above';
  1012. // @breaking-change 15.0.0 Remove null check for `ngZone`.
  1013. // `positionChanges` fires outside of the `ngZone` and `setPositionClasses` might be
  1014. // updating something in the view so we need to bring it back in.
  1015. if (this._ngZone) {
  1016. this._ngZone.run(() => menu.setPositionClasses(posX, posY));
  1017. }
  1018. else {
  1019. menu.setPositionClasses(posX, posY);
  1020. }
  1021. });
  1022. }
  1023. }
  1024. /**
  1025. * Sets the appropriate positions on a position strategy
  1026. * so the overlay connects with the trigger correctly.
  1027. * @param positionStrategy Strategy whose position to update.
  1028. */
  1029. _setPosition(menu, positionStrategy) {
  1030. let [originX, originFallbackX] = menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'];
  1031. let [overlayY, overlayFallbackY] = menu.yPosition === 'above' ? ['bottom', 'top'] : ['top', 'bottom'];
  1032. let [originY, originFallbackY] = [overlayY, overlayFallbackY];
  1033. let [overlayX, overlayFallbackX] = [originX, originFallbackX];
  1034. let offsetY = 0;
  1035. if (this.triggersSubmenu()) {
  1036. // When the menu is a sub-menu, it should always align itself
  1037. // to the edges of the trigger, instead of overlapping it.
  1038. overlayFallbackX = originX = menu.xPosition === 'before' ? 'start' : 'end';
  1039. originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
  1040. if (this._parentMaterialMenu) {
  1041. if (this._parentInnerPadding == null) {
  1042. const firstItem = this._parentMaterialMenu.items.first;
  1043. this._parentInnerPadding = firstItem ? firstItem._getHostElement().offsetTop : 0;
  1044. }
  1045. offsetY = overlayY === 'bottom' ? this._parentInnerPadding : -this._parentInnerPadding;
  1046. }
  1047. }
  1048. else if (!menu.overlapTrigger) {
  1049. originY = overlayY === 'top' ? 'bottom' : 'top';
  1050. originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';
  1051. }
  1052. positionStrategy.withPositions([
  1053. { originX, originY, overlayX, overlayY, offsetY },
  1054. { originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY },
  1055. {
  1056. originX,
  1057. originY: originFallbackY,
  1058. overlayX,
  1059. overlayY: overlayFallbackY,
  1060. offsetY: -offsetY,
  1061. },
  1062. {
  1063. originX: originFallbackX,
  1064. originY: originFallbackY,
  1065. overlayX: overlayFallbackX,
  1066. overlayY: overlayFallbackY,
  1067. offsetY: -offsetY,
  1068. },
  1069. ]);
  1070. }
  1071. /** Returns a stream that emits whenever an action that should close the menu occurs. */
  1072. _menuClosingActions() {
  1073. const backdrop = this._overlayRef.backdropClick();
  1074. const detachments = this._overlayRef.detachments();
  1075. const parentClose = this._parentMaterialMenu ? this._parentMaterialMenu.closed : of();
  1076. const hover = this._parentMaterialMenu
  1077. ? this._parentMaterialMenu._hovered().pipe(filter(active => active !== this._menuItemInstance), filter(() => this._menuOpen))
  1078. : of();
  1079. return merge(backdrop, parentClose, hover, detachments);
  1080. }
  1081. /** Handles mouse presses on the trigger. */
  1082. _handleMousedown(event) {
  1083. if (!isFakeMousedownFromScreenReader(event)) {
  1084. // Since right or middle button clicks won't trigger the `click` event,
  1085. // we shouldn't consider the menu as opened by mouse in those cases.
  1086. this._openedBy = event.button === 0 ? 'mouse' : undefined;
  1087. // Since clicking on the trigger won't close the menu if it opens a sub-menu,
  1088. // we should prevent focus from moving onto it via click to avoid the
  1089. // highlight from lingering on the menu item.
  1090. if (this.triggersSubmenu()) {
  1091. event.preventDefault();
  1092. }
  1093. }
  1094. }
  1095. /** Handles key presses on the trigger. */
  1096. _handleKeydown(event) {
  1097. const keyCode = event.keyCode;
  1098. // Pressing enter on the trigger will trigger the click handler later.
  1099. if (keyCode === ENTER || keyCode === SPACE) {
  1100. this._openedBy = 'keyboard';
  1101. }
  1102. if (this.triggersSubmenu() &&
  1103. ((keyCode === RIGHT_ARROW && this.dir === 'ltr') ||
  1104. (keyCode === LEFT_ARROW && this.dir === 'rtl'))) {
  1105. this._openedBy = 'keyboard';
  1106. this.openMenu();
  1107. }
  1108. }
  1109. /** Handles click events on the trigger. */
  1110. _handleClick(event) {
  1111. if (this.triggersSubmenu()) {
  1112. // Stop event propagation to avoid closing the parent menu.
  1113. event.stopPropagation();
  1114. this.openMenu();
  1115. }
  1116. else {
  1117. this.toggleMenu();
  1118. }
  1119. }
  1120. /** Handles the cases where the user hovers over the trigger. */
  1121. _handleHover() {
  1122. // Subscribe to changes in the hovered item in order to toggle the panel.
  1123. if (!this.triggersSubmenu() || !this._parentMaterialMenu) {
  1124. return;
  1125. }
  1126. this._hoverSubscription = this._parentMaterialMenu
  1127. ._hovered()
  1128. // Since we might have multiple competing triggers for the same menu (e.g. a sub-menu
  1129. // with different data and triggers), we have to delay it by a tick to ensure that
  1130. // it won't be closed immediately after it is opened.
  1131. .pipe(filter(active => active === this._menuItemInstance && !active.disabled), delay(0, asapScheduler))
  1132. .subscribe(() => {
  1133. this._openedBy = 'mouse';
  1134. // If the same menu is used between multiple triggers, it might still be animating
  1135. // while the new trigger tries to re-open it. Wait for the animation to finish
  1136. // before doing so. Also interrupt if the user moves to another item.
  1137. if (this.menu instanceof _MatMenuBase && this.menu._isAnimating) {
  1138. // We need the `delay(0)` here in order to avoid
  1139. // 'changed after checked' errors in some cases. See #12194.
  1140. this.menu._animationDone
  1141. .pipe(take(1), delay(0, asapScheduler), takeUntil(this._parentMaterialMenu._hovered()))
  1142. .subscribe(() => this.openMenu());
  1143. }
  1144. else {
  1145. this.openMenu();
  1146. }
  1147. });
  1148. }
  1149. /** Gets the portal that should be attached to the overlay. */
  1150. _getPortal(menu) {
  1151. // Note that we can avoid this check by keeping the portal on the menu panel.
  1152. // While it would be cleaner, we'd have to introduce another required method on
  1153. // `MatMenuPanel`, making it harder to consume.
  1154. if (!this._portal || this._portal.templateRef !== menu.templateRef) {
  1155. this._portal = new TemplatePortal(menu.templateRef, this._viewContainerRef);
  1156. }
  1157. return this._portal;
  1158. }
  1159. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatMenuTriggerBase, deps: [{ token: i1$1.Overlay }, { token: i0.ElementRef }, { token: i0.ViewContainerRef }, { token: MAT_MENU_SCROLL_STRATEGY }, { token: MAT_MENU_PANEL, optional: true }, { token: MatMenuItem, optional: true, self: true }, { token: i3$1.Directionality, optional: true }, { token: i1.FocusMonitor }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
  1160. static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: _MatMenuTriggerBase, inputs: { _deprecatedMatMenuTriggerFor: ["mat-menu-trigger-for", "_deprecatedMatMenuTriggerFor"], menu: ["matMenuTriggerFor", "menu"], menuData: ["matMenuTriggerData", "menuData"], restoreFocus: ["matMenuTriggerRestoreFocus", "restoreFocus"] }, outputs: { menuOpened: "menuOpened", onMenuOpen: "onMenuOpen", menuClosed: "menuClosed", onMenuClose: "onMenuClose" }, host: { listeners: { "click": "_handleClick($event)", "mousedown": "_handleMousedown($event)", "keydown": "_handleKeydown($event)" }, properties: { "attr.aria-haspopup": "menu ? \"menu\" : null", "attr.aria-expanded": "menuOpen", "attr.aria-controls": "menuOpen ? menu.panelId : null" } }, ngImport: i0 }); }
  1161. }
  1162. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatMenuTriggerBase, decorators: [{
  1163. type: Directive,
  1164. args: [{
  1165. host: {
  1166. '[attr.aria-haspopup]': 'menu ? "menu" : null',
  1167. '[attr.aria-expanded]': 'menuOpen',
  1168. '[attr.aria-controls]': 'menuOpen ? menu.panelId : null',
  1169. '(click)': '_handleClick($event)',
  1170. '(mousedown)': '_handleMousedown($event)',
  1171. '(keydown)': '_handleKeydown($event)',
  1172. },
  1173. }]
  1174. }], ctorParameters: function () { return [{ type: i1$1.Overlay }, { type: i0.ElementRef }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
  1175. type: Inject,
  1176. args: [MAT_MENU_SCROLL_STRATEGY]
  1177. }] }, { type: undefined, decorators: [{
  1178. type: Inject,
  1179. args: [MAT_MENU_PANEL]
  1180. }, {
  1181. type: Optional
  1182. }] }, { type: MatMenuItem, decorators: [{
  1183. type: Optional
  1184. }, {
  1185. type: Self
  1186. }] }, { type: i3$1.Directionality, decorators: [{
  1187. type: Optional
  1188. }] }, { type: i1.FocusMonitor }, { type: i0.NgZone }]; }, propDecorators: { _deprecatedMatMenuTriggerFor: [{
  1189. type: Input,
  1190. args: ['mat-menu-trigger-for']
  1191. }], menu: [{
  1192. type: Input,
  1193. args: ['matMenuTriggerFor']
  1194. }], menuData: [{
  1195. type: Input,
  1196. args: ['matMenuTriggerData']
  1197. }], restoreFocus: [{
  1198. type: Input,
  1199. args: ['matMenuTriggerRestoreFocus']
  1200. }], menuOpened: [{
  1201. type: Output
  1202. }], onMenuOpen: [{
  1203. type: Output
  1204. }], menuClosed: [{
  1205. type: Output
  1206. }], onMenuClose: [{
  1207. type: Output
  1208. }] } });
  1209. /** Directive applied to an element that should trigger a `mat-menu`. */
  1210. class MatMenuTrigger extends _MatMenuTriggerBase {
  1211. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuTrigger, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
  1212. static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", host: { classAttribute: "mat-mdc-menu-trigger" }, exportAs: ["matMenuTrigger"], usesInheritance: true, ngImport: i0 }); }
  1213. }
  1214. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuTrigger, decorators: [{
  1215. type: Directive,
  1216. args: [{
  1217. selector: `[mat-menu-trigger-for], [matMenuTriggerFor]`,
  1218. host: {
  1219. 'class': 'mat-mdc-menu-trigger',
  1220. },
  1221. exportAs: 'matMenuTrigger',
  1222. }]
  1223. }] });
  1224. class MatMenuModule {
  1225. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
  1226. static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.0", ngImport: i0, type: MatMenuModule, declarations: [MatMenu, MatMenuItem, MatMenuContent, MatMenuTrigger], imports: [CommonModule, MatRippleModule, MatCommonModule, OverlayModule], exports: [CdkScrollableModule,
  1227. MatMenu,
  1228. MatCommonModule,
  1229. MatMenuItem,
  1230. MatMenuContent,
  1231. MatMenuTrigger] }); }
  1232. static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuModule, providers: [MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER], imports: [CommonModule, MatRippleModule, MatCommonModule, OverlayModule, CdkScrollableModule,
  1233. MatCommonModule] }); }
  1234. }
  1235. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatMenuModule, decorators: [{
  1236. type: NgModule,
  1237. args: [{
  1238. imports: [CommonModule, MatRippleModule, MatCommonModule, OverlayModule],
  1239. exports: [
  1240. CdkScrollableModule,
  1241. MatMenu,
  1242. MatCommonModule,
  1243. MatMenuItem,
  1244. MatMenuContent,
  1245. MatMenuTrigger,
  1246. ],
  1247. declarations: [MatMenu, MatMenuItem, MatMenuContent, MatMenuTrigger],
  1248. providers: [MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER],
  1249. }]
  1250. }] });
  1251. /**
  1252. * Generated bundle index. Do not edit.
  1253. */
  1254. export { MAT_MENU_CONTENT, MAT_MENU_DEFAULT_OPTIONS, MAT_MENU_PANEL, MAT_MENU_SCROLL_STRATEGY, MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER, MENU_PANEL_TOP_PADDING, MatMenu, MatMenuContent, MatMenuItem, MatMenuModule, MatMenuTrigger, _MatMenuBase, _MatMenuContentBase, _MatMenuTriggerBase, fadeInItems, matMenuAnimations, transformMenu };
  1255. //# sourceMappingURL=menu.mjs.map