source: trip-planner-front/node_modules/@angular/material/fesm2015/menu.js@ 6a3a178

Last change on this file since 6a3a178 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

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