menu.mjs 70 KB

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