@angular/material
Version:
Angular Material
1,318 lines (1,304 loc) • 52.9 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { A11yModule, FocusKeyManager, FocusMonitor, isFakeMousedownFromScreenReader } from '@angular/cdk/a11y';
import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
import { CommonModule, DOCUMENT } from '@angular/common';
import { ApplicationRef, ChangeDetectionStrategy, Component, ComponentFactoryResolver, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, Inject, InjectionToken, Injector, Input, NgModule, NgZone, Optional, Output, Self, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { MatCommonModule, MatRippleModule, mixinDisableRipple, mixinDisabled } from '@angular/material/core';
import { DomPortalOutlet, PortalModule, TemplatePortal } from '@angular/cdk/portal';
import { ESCAPE, LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import { startWith } from 'rxjs/operators/startWith';
import { switchMap } from 'rxjs/operators/switchMap';
import { take } from 'rxjs/operators/take';
import { merge } from 'rxjs/observable/merge';
import { Subscription } from 'rxjs/Subscription';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { __extends } from 'tslib';
import * as tslib_1 from 'tslib';
import { Subject } from 'rxjs/Subject';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Directionality } from '@angular/cdk/bidi';
import { filter } from 'rxjs/operators/filter';
import { of } from 'rxjs/observable/of';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Animations used by the mat-menu component.
* Animation duration and timing values are based on:
* https://material.io/guidelines/components/menus.html#menus-usage
*/
var matMenuAnimations = {
/**
* This animation controls the menu panel's entry and exit from the page.
*
* When the menu panel is added to the DOM, it scales in and fades in its border.
*
* When the menu panel is removed from the DOM, it simply fades out after a brief
* delay to display the ripple.
*/
transformMenu: trigger('transformMenu', [
// TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out.
state('void', style({
opacity: 0,
// This starts off from 0.01, instead of 0, because there's an issue in the Angular animations
// as of 4.2, which causes the animation to be skipped if it starts from 0.
transform: 'scale(0.01, 0.01)'
})),
state('enter-start', style({
opacity: 1,
transform: 'scale(1, 0.5)'
})),
state('enter', style({
transform: 'scale(1, 1)'
})),
transition('void => enter-start', animate('100ms linear')),
transition('enter-start => enter', animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)')),
transition('* => void', animate('150ms 50ms linear', style({ opacity: 0 })))
]),
/**
* This animation fades in the background color and content of the menu panel
* after its containing element is scaled in.
*/
fadeInItems: trigger('fadeInItems', [
state('showing', style({ opacity: 1 })),
transition('void => *', [
style({ opacity: 0 }),
animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')
])
])
};
/**
* @deprecated
* \@deletion-target 7.0.0
*/
var fadeInItems = matMenuAnimations.fadeInItems;
/**
* @deprecated
* \@deletion-target 7.0.0
*/
var transformMenu = matMenuAnimations.transformMenu;
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Throws an exception for the case when menu trigger doesn't have a valid mat-menu instance
* \@docs-private
* @return {?}
*/
function throwMatMenuMissingError() {
throw Error("mat-menu-trigger: must pass in an mat-menu instance.\n\n Example:\n <mat-menu #menu=\"matMenu\"></mat-menu>\n <button [matMenuTriggerFor]=\"menu\"></button>");
}
/**
* Throws an exception for the case when menu's x-position value isn't valid.
* In other words, it doesn't match 'before' or 'after'.
* \@docs-private
* @return {?}
*/
function throwMatMenuInvalidPositionX() {
throw Error("x-position value must be either 'before' or after'.\n Example: <mat-menu x-position=\"before\" #menu=\"matMenu\"></mat-menu>");
}
/**
* Throws an exception for the case when menu's y-position value isn't valid.
* In other words, it doesn't match 'above' or 'below'.
* \@docs-private
* @return {?}
*/
function throwMatMenuInvalidPositionY() {
throw Error("y-position value must be either 'above' or below'.\n Example: <mat-menu y-position=\"above\" #menu=\"matMenu\"></mat-menu>");
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* \@docs-private
*/
var MatMenuItemBase = /** @class */ (function () {
function MatMenuItemBase() {
}
return MatMenuItemBase;
}());
var _MatMenuItemMixinBase = mixinDisableRipple(mixinDisabled(MatMenuItemBase));
/**
* This directive is intended to be used inside an mat-menu tag.
* It exists mostly to set the role attribute.
*/
var MatMenuItem = /** @class */ (function (_super) {
__extends(MatMenuItem, _super);
function MatMenuItem(_elementRef, document, _focusMonitor) {
var _this =
// @deletion-target 6.0.0 make `_focusMonitor` and `document` required params.
_super.call(this) || this;
_this._elementRef = _elementRef;
_this._focusMonitor = _focusMonitor;
/**
* Stream that emits when the menu item is hovered.
*/
_this._hovered = new Subject();
/**
* Whether the menu item is highlighted.
*/
_this._highlighted = false;
/**
* Whether the menu item acts as a trigger for a sub-menu.
*/
_this._triggersSubmenu = false;
if (_focusMonitor) {
// Start monitoring the element so it gets the appropriate focused classes. We want
// to show the focus style for menu items only when the focus was not caused by a
// mouse or touch interaction.
_focusMonitor.monitor(_this._getHostElement(), false);
}
_this._document = document;
return _this;
}
/** Focuses the menu item. */
/**
* Focuses the menu item.
* @param {?=} origin
* @return {?}
*/
MatMenuItem.prototype.focus = /**
* Focuses the menu item.
* @param {?=} origin
* @return {?}
*/
function (origin) {
if (origin === void 0) { origin = 'program'; }
if (this._focusMonitor) {
this._focusMonitor.focusVia(this._getHostElement(), origin);
}
else {
this._getHostElement().focus();
}
};
/**
* @return {?}
*/
MatMenuItem.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
if (this._focusMonitor) {
this._focusMonitor.stopMonitoring(this._getHostElement());
}
this._hovered.complete();
};
/** Used to set the `tabindex`. */
/**
* Used to set the `tabindex`.
* @return {?}
*/
MatMenuItem.prototype._getTabIndex = /**
* Used to set the `tabindex`.
* @return {?}
*/
function () {
return this.disabled ? '-1' : '0';
};
/** Returns the host DOM element. */
/**
* Returns the host DOM element.
* @return {?}
*/
MatMenuItem.prototype._getHostElement = /**
* Returns the host DOM element.
* @return {?}
*/
function () {
return this._elementRef.nativeElement;
};
/** Prevents the default element actions if it is disabled. */
/**
* Prevents the default element actions if it is disabled.
* @param {?} event
* @return {?}
*/
MatMenuItem.prototype._checkDisabled = /**
* Prevents the default element actions if it is disabled.
* @param {?} event
* @return {?}
*/
function (event) {
if (this.disabled) {
event.preventDefault();
event.stopPropagation();
}
};
/** Emits to the hover stream. */
/**
* Emits to the hover stream.
* @return {?}
*/
MatMenuItem.prototype._emitHoverEvent = /**
* Emits to the hover stream.
* @return {?}
*/
function () {
if (!this.disabled) {
this._hovered.next(this);
}
};
/** Gets the label to be used when determining whether the option should be focused. */
/**
* Gets the label to be used when determining whether the option should be focused.
* @return {?}
*/
MatMenuItem.prototype.getLabel = /**
* Gets the label to be used when determining whether the option should be focused.
* @return {?}
*/
function () {
var /** @type {?} */ element = this._elementRef.nativeElement;
var /** @type {?} */ textNodeType = this._document ? this._document.TEXT_NODE : 3;
var /** @type {?} */ output = '';
if (element.childNodes) {
var /** @type {?} */ length_1 = element.childNodes.length;
// Go through all the top-level text nodes and extract their text.
// We skip anything that's not a text node to prevent the text from
// being thrown off by something like an icon.
for (var /** @type {?} */ i = 0; i < length_1; i++) {
if (element.childNodes[i].nodeType === textNodeType) {
output += element.childNodes[i].textContent;
}
}
}
return output.trim();
};
MatMenuItem.decorators = [
{ type: Component, args: [{selector: '[mat-menu-item]',
exportAs: 'matMenuItem',
inputs: ['disabled', 'disableRipple'],
host: {
'role': 'menuitem',
'class': 'mat-menu-item',
'[class.mat-menu-item-highlighted]': '_highlighted',
'[class.mat-menu-item-submenu-trigger]': '_triggersSubmenu',
'[attr.tabindex]': '_getTabIndex()',
'[attr.aria-disabled]': 'disabled.toString()',
'[attr.disabled]': 'disabled || null',
'(click)': '_checkDisabled($event)',
'(mouseenter)': '_emitHoverEvent()',
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
template: "<ng-content></ng-content><div class=\"mat-menu-ripple\" matRipple [matRippleDisabled]=\"disableRipple || disabled\" [matRippleTrigger]=\"_getHostElement()\"></div>",
},] },
];
/** @nocollapse */
MatMenuItem.ctorParameters = function () { return [
{ type: ElementRef, },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] },] },
{ type: FocusMonitor, },
]; };
return MatMenuItem;
}(_MatMenuItemMixinBase));
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Menu content that will be rendered lazily once the menu is opened.
*/
var MatMenuContent = /** @class */ (function () {
function MatMenuContent(_template, _componentFactoryResolver, _appRef, _injector, _viewContainerRef, _document) {
this._template = _template;
this._componentFactoryResolver = _componentFactoryResolver;
this._appRef = _appRef;
this._injector = _injector;
this._viewContainerRef = _viewContainerRef;
this._document = _document;
}
/**
* Attaches the content with a particular context.
* @docs-private
*/
/**
* Attaches the content with a particular context.
* \@docs-private
* @param {?=} context
* @return {?}
*/
MatMenuContent.prototype.attach = /**
* Attaches the content with a particular context.
* \@docs-private
* @param {?=} context
* @return {?}
*/
function (context) {
if (context === void 0) { context = {}; }
if (!this._portal) {
this._portal = new TemplatePortal(this._template, this._viewContainerRef);
}
else if (this._portal.isAttached) {
this._portal.detach();
}
if (!this._outlet) {
this._outlet = new DomPortalOutlet(this._document.createElement('div'), this._componentFactoryResolver, this._appRef, this._injector);
}
var /** @type {?} */ element = this._template.elementRef.nativeElement; /** @type {?} */
((
// Because we support opening the same menu from different triggers (which in turn have their
// own `OverlayRef` panel), we have to re-insert the host element every time, otherwise we
// risk it staying attached to a pane that's no longer in the DOM.
element.parentNode)).insertBefore(this._outlet.outletElement, element);
this._portal.attach(this._outlet, context);
};
/**
* @return {?}
*/
MatMenuContent.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
if (this._outlet) {
this._outlet.dispose();
}
};
MatMenuContent.decorators = [
{ type: Directive, args: [{
selector: 'ng-template[matMenuContent]'
},] },
];
/** @nocollapse */
MatMenuContent.ctorParameters = function () { return [
{ type: TemplateRef, },
{ type: ComponentFactoryResolver, },
{ type: ApplicationRef, },
{ type: Injector, },
{ type: ViewContainerRef, },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] },] },
]; };
return MatMenuContent;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Default `mat-menu` options that can be overridden.
* @record
*/
/**
* Injection token to be used to override the default options for `mat-menu`.
*/
var MAT_MENU_DEFAULT_OPTIONS = new InjectionToken('mat-menu-default-options');
/**
* Start elevation for the menu panel.
* \@docs-private
*/
var MAT_MENU_BASE_ELEVATION = 2;
var MatMenu = /** @class */ (function () {
function MatMenu(_elementRef, _ngZone, _defaultOptions) {
this._elementRef = _elementRef;
this._ngZone = _ngZone;
this._defaultOptions = _defaultOptions;
this._xPosition = this._defaultOptions.xPosition;
this._yPosition = this._defaultOptions.yPosition;
/**
* Subscription to tab events on the menu panel
*/
this._tabSubscription = Subscription.EMPTY;
/**
* Config object to be passed into the menu's ngClass
*/
this._classList = {};
/**
* Current state of the panel animation.
*/
this._panelAnimationState = 'void';
this._overlapTrigger = this._defaultOptions.overlapTrigger;
/**
* Event emitted when the menu is closed.
*/
this.closed = new EventEmitter();
/**
* Event emitted when the menu is closed.
* @deprecated Switch to `closed` instead
* \@deletion-target 6.0.0
*/
this.close = this.closed;
}
Object.defineProperty(MatMenu.prototype, "xPosition", {
get: /**
* Position of the menu in the X axis.
* @return {?}
*/
function () { return this._xPosition; },
set: /**
* @param {?} value
* @return {?}
*/
function (value) {
if (value !== 'before' && value !== 'after') {
throwMatMenuInvalidPositionX();
}
this._xPosition = value;
this.setPositionClasses();
},
enumerable: true,
configurable: true
});
Object.defineProperty(MatMenu.prototype, "yPosition", {
get: /**
* Position of the menu in the Y axis.
* @return {?}
*/
function () { return this._yPosition; },
set: /**
* @param {?} value
* @return {?}
*/
function (value) {
if (value !== 'above' && value !== 'below') {
throwMatMenuInvalidPositionY();
}
this._yPosition = value;
this.setPositionClasses();
},
enumerable: true,
configurable: true
});
Object.defineProperty(MatMenu.prototype, "overlapTrigger", {
get: /**
* Whether the menu should overlap its trigger.
* @return {?}
*/
function () { return this._overlapTrigger; },
set: /**
* @param {?} value
* @return {?}
*/
function (value) {
this._overlapTrigger = coerceBooleanProperty(value);
},
enumerable: true,
configurable: true
});
Object.defineProperty(MatMenu.prototype, "panelClass", {
set: /**
* This method takes classes set on the host mat-menu element and applies them on the
* menu template that displays in the overlay container. Otherwise, it's difficult
* to style the containing menu from outside the component.
* @param {?} classes list of class names
* @return {?}
*/
function (classes) {
if (classes && classes.length) {
this._classList = classes.split(' ').reduce(function (obj, className) {
obj[className] = true;
return obj;
}, {});
this._elementRef.nativeElement.className = '';
this.setPositionClasses();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(MatMenu.prototype, "classList", {
get: /**
* This method takes classes set on the host mat-menu element and applies them on the
* menu template that displays in the overlay container. Otherwise, it's difficult
* to style the containing menu from outside the component.
* @deprecated Use `panelClass` instead.
* \@deletion-target 6.0.0
* @return {?}
*/
function () { return this.panelClass; },
set: /**
* @param {?} classes
* @return {?}
*/
function (classes) { this.panelClass = classes; },
enumerable: true,
configurable: true
});
/**
* @return {?}
*/
MatMenu.prototype.ngAfterContentInit = /**
* @return {?}
*/
function () {
var _this = this;
this._keyManager = new FocusKeyManager(this.items).withWrap().withTypeAhead();
this._tabSubscription = this._keyManager.tabOut.subscribe(function () { return _this.close.emit('keydown'); });
};
/**
* @return {?}
*/
MatMenu.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
this._tabSubscription.unsubscribe();
this.closed.complete();
};
/** Stream that emits whenever the hovered menu item changes. */
/**
* Stream that emits whenever the hovered menu item changes.
* @return {?}
*/
MatMenu.prototype._hovered = /**
* Stream that emits whenever the hovered menu item changes.
* @return {?}
*/
function () {
var _this = this;
if (this.items) {
return this.items.changes.pipe(startWith(this.items), switchMap(function (items) { return merge.apply(void 0, items.map(function (item) { return item._hovered; })); }));
}
return this._ngZone.onStable
.asObservable()
.pipe(take(1), switchMap(function () { return _this._hovered(); }));
};
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
/**
* Handle a keyboard event from the menu, delegating to the appropriate action.
* @param {?} event
* @return {?}
*/
MatMenu.prototype._handleKeydown = /**
* Handle a keyboard event from the menu, delegating to the appropriate action.
* @param {?} event
* @return {?}
*/
function (event) {
switch (event.keyCode) {
case ESCAPE:
this.closed.emit('keydown');
event.stopPropagation();
break;
case LEFT_ARROW:
if (this.parentMenu && this.direction === 'ltr') {
this.closed.emit('keydown');
}
break;
case RIGHT_ARROW:
if (this.parentMenu && this.direction === 'rtl') {
this.closed.emit('keydown');
}
break;
default:
this._keyManager.onKeydown(event);
}
};
/**
* Focus the first item in the menu.
* @param origin Action from which the focus originated. Used to set the correct styling.
*/
/**
* Focus the first item in the menu.
* @param {?=} origin Action from which the focus originated. Used to set the correct styling.
* @return {?}
*/
MatMenu.prototype.focusFirstItem = /**
* Focus the first item in the menu.
* @param {?=} origin Action from which the focus originated. Used to set the correct styling.
* @return {?}
*/
function (origin) {
var _this = this;
if (origin === void 0) { origin = 'program'; }
// When the content is rendered lazily, it takes a bit before the items are inside the DOM.
if (this.lazyContent) {
this._ngZone.onStable.asObservable()
.pipe(take(1))
.subscribe(function () { return _this._keyManager.setFocusOrigin(origin).setFirstItemActive(); });
}
else {
this._keyManager.setFocusOrigin(origin).setFirstItemActive();
}
};
/**
* Resets the active item in the menu. This is used when the menu is opened, allowing
* the user to start from the first option when pressing the down arrow.
*/
/**
* Resets the active item in the menu. This is used when the menu is opened, allowing
* the user to start from the first option when pressing the down arrow.
* @return {?}
*/
MatMenu.prototype.resetActiveItem = /**
* Resets the active item in the menu. This is used when the menu is opened, allowing
* the user to start from the first option when pressing the down arrow.
* @return {?}
*/
function () {
this._keyManager.setActiveItem(-1);
};
/**
* It's necessary to set position-based classes to ensure the menu panel animation
* folds out from the correct direction.
*/
/**
* It's necessary to set position-based classes to ensure the menu panel animation
* folds out from the correct direction.
* @param {?=} posX
* @param {?=} posY
* @return {?}
*/
MatMenu.prototype.setPositionClasses = /**
* It's necessary to set position-based classes to ensure the menu panel animation
* folds out from the correct direction.
* @param {?=} posX
* @param {?=} posY
* @return {?}
*/
function (posX, posY) {
if (posX === void 0) { posX = this.xPosition; }
if (posY === void 0) { posY = this.yPosition; }
this._classList['mat-menu-before'] = posX === 'before';
this._classList['mat-menu-after'] = posX === 'after';
this._classList['mat-menu-above'] = posY === 'above';
this._classList['mat-menu-below'] = posY === 'below';
};
/**
* Sets the menu panel elevation.
* @param depth Number of parent menus that come before the menu.
*/
/**
* Sets the menu panel elevation.
* @param {?} depth Number of parent menus that come before the menu.
* @return {?}
*/
MatMenu.prototype.setElevation = /**
* Sets the menu panel elevation.
* @param {?} depth Number of parent menus that come before the menu.
* @return {?}
*/
function (depth) {
// The elevation starts at the base and increases by one for each level.
var /** @type {?} */ newElevation = "mat-elevation-z" + (MAT_MENU_BASE_ELEVATION + depth);
var /** @type {?} */ customElevation = Object.keys(this._classList).find(function (c) { return c.startsWith('mat-elevation-z'); });
if (!customElevation || customElevation === this._previousElevation) {
if (this._previousElevation) {
this._classList[this._previousElevation] = false;
}
this._classList[newElevation] = true;
this._previousElevation = newElevation;
}
};
/** Starts the enter animation. */
/**
* Starts the enter animation.
* @return {?}
*/
MatMenu.prototype._startAnimation = /**
* Starts the enter animation.
* @return {?}
*/
function () {
this._panelAnimationState = 'enter-start';
};
/** Resets the panel animation to its initial state. */
/**
* Resets the panel animation to its initial state.
* @return {?}
*/
MatMenu.prototype._resetAnimation = /**
* Resets the panel animation to its initial state.
* @return {?}
*/
function () {
this._panelAnimationState = 'void';
};
/** Callback that is invoked when the panel animation completes. */
/**
* Callback that is invoked when the panel animation completes.
* @param {?} event
* @return {?}
*/
MatMenu.prototype._onAnimationDone = /**
* Callback that is invoked when the panel animation completes.
* @param {?} event
* @return {?}
*/
function (event) {
// After the initial expansion is done, trigger the second phase of the enter animation.
if (event.toState === 'enter-start') {
this._panelAnimationState = 'enter';
}
};
MatMenu.decorators = [
{ type: Component, args: [{selector: 'mat-menu',
template: "<ng-template><div class=\"mat-menu-panel\" [ngClass]=\"_classList\" (keydown)=\"_handleKeydown($event)\" (click)=\"closed.emit('click')\" [@transformMenu]=\"_panelAnimationState\" (@transformMenu.done)=\"_onAnimationDone($event)\" tabindex=\"-1\" role=\"menu\"><div class=\"mat-menu-content\" [@fadeInItems]=\"'showing'\"><ng-content></ng-content></div></div></ng-template>",
styles: [".mat-menu-panel{-webkit-backface-visibility:hidden;backface-visibility:hidden;min-width:112px;max-width:280px;overflow:auto;-webkit-overflow-scrolling:touch;max-height:calc(100vh - 48px);border-radius:2px;outline:0}.mat-menu-panel:not([class*=mat-elevation-z]){box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mat-menu-panel.mat-menu-after.mat-menu-below{transform-origin:left top}.mat-menu-panel.mat-menu-after.mat-menu-above{transform-origin:left bottom}.mat-menu-panel.mat-menu-before.mat-menu-below{transform-origin:right top}.mat-menu-panel.mat-menu-before.mat-menu-above{transform-origin:right bottom}[dir=rtl] .mat-menu-panel.mat-menu-after.mat-menu-below{transform-origin:right top}[dir=rtl] .mat-menu-panel.mat-menu-after.mat-menu-above{transform-origin:right bottom}[dir=rtl] .mat-menu-panel.mat-menu-before.mat-menu-below{transform-origin:left top}[dir=rtl] .mat-menu-panel.mat-menu-before.mat-menu-above{transform-origin:left bottom}.mat-menu-panel.ng-animating{pointer-events:none}@media screen and (-ms-high-contrast:active){.mat-menu-panel{outline:solid 1px}}.mat-menu-content{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:0;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;position:relative}.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}[dir=rtl] .mat-menu-item .mat-icon{margin-left:16px;margin-right:0}.mat-menu-item-submenu-trigger{padding-right:32px}.mat-menu-item-submenu-trigger::after{width:0;height:0;border-style:solid;border-width:5px 0 5px 5px;border-color:transparent transparent transparent currentColor;content:'';display:inline-block;position:absolute;top:50%;right:16px;transform:translateY(-50%)}[dir=rtl] .mat-menu-item-submenu-trigger{padding-right:16px;padding-left:32px}[dir=rtl] .mat-menu-item-submenu-trigger::after{right:auto;left:16px;transform:rotateY(180deg) translateY(-50%)}button.mat-menu-item{width:100%}.mat-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}"],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
animations: [
matMenuAnimations.transformMenu,
matMenuAnimations.fadeInItems
],
exportAs: 'matMenu'
},] },
];
/** @nocollapse */
MatMenu.ctorParameters = function () { return [
{ type: ElementRef, },
{ type: NgZone, },
{ type: undefined, decorators: [{ type: Inject, args: [MAT_MENU_DEFAULT_OPTIONS,] },] },
]; };
MatMenu.propDecorators = {
"xPosition": [{ type: Input },],
"yPosition": [{ type: Input },],
"templateRef": [{ type: ViewChild, args: [TemplateRef,] },],
"items": [{ type: ContentChildren, args: [MatMenuItem, { descendants: true },] },],
"lazyContent": [{ type: ContentChild, args: [MatMenuContent,] },],
"overlapTrigger": [{ type: Input },],
"panelClass": [{ type: Input, args: ['class',] },],
"classList": [{ type: Input },],
"closed": [{ type: Output },],
"close": [{ type: Output },],
};
return MatMenu;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Injection token that determines the scroll handling while the menu is open.
*/
var MAT_MENU_SCROLL_STRATEGY = new InjectionToken('mat-menu-scroll-strategy');
/**
* \@docs-private
* @param {?} overlay
* @return {?}
*/
function MAT_MENU_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
return function () { return overlay.scrollStrategies.reposition(); };
}
/**
* \@docs-private
*/
var MAT_MENU_SCROLL_STRATEGY_PROVIDER = {
provide: MAT_MENU_SCROLL_STRATEGY,
deps: [Overlay],
useFactory: MAT_MENU_SCROLL_STRATEGY_PROVIDER_FACTORY,
};
/**
* Default top padding of the menu panel.
*/
var MENU_PANEL_TOP_PADDING = 8;
/**
* This directive is intended to be used in conjunction with an mat-menu tag. It is
* responsible for toggling the display of the provided menu instance.
*/
var MatMenuTrigger = /** @class */ (function () {
function MatMenuTrigger(_overlay, _element, _viewContainerRef, _scrollStrategy, _parentMenu, _menuItemInstance, _dir, _focusMonitor) {
this._overlay = _overlay;
this._element = _element;
this._viewContainerRef = _viewContainerRef;
this._scrollStrategy = _scrollStrategy;
this._parentMenu = _parentMenu;
this._menuItemInstance = _menuItemInstance;
this._dir = _dir;
this._focusMonitor = _focusMonitor;
this._overlayRef = null;
this._menuOpen = false;
this._closeSubscription = Subscription.EMPTY;
this._hoverSubscription = Subscription.EMPTY;
this._openedByMouse = false;
/**
* Event emitted when the associated menu is opened.
*/
this.menuOpened = new EventEmitter();
/**
* Event emitted when the associated menu is opened.
* @deprecated Switch to `menuOpened` instead
* \@deletion-target 6.0.0
*/
this.onMenuOpen = this.menuOpened;
/**
* Event emitted when the associated menu is closed.
*/
this.menuClosed = new EventEmitter();
/**
* Event emitted when the associated menu is closed.
* @deprecated Switch to `menuClosed` instead
* \@deletion-target 6.0.0
*/
this.onMenuClose = this.menuClosed;
if (_menuItemInstance) {
_menuItemInstance._triggersSubmenu = this.triggersSubmenu();
}
}
Object.defineProperty(MatMenuTrigger.prototype, "_deprecatedMatMenuTriggerFor", {
get: /**
* @deprecated
* \@deletion-target 6.0.0
* @return {?}
*/
function () {
return this.menu;
},
set: /**
* @param {?} v
* @return {?}
*/
function (v) {
this.menu = v;
},
enumerable: true,
configurable: true
});
/**
* @return {?}
*/
MatMenuTrigger.prototype.ngAfterContentInit = /**
* @return {?}
*/
function () {
var _this = this;
this._checkMenu();
this.menu.close.subscribe(function (reason) {
_this._destroyMenu();
// If a click closed the menu, we should close the entire chain of nested menus.
if (reason === 'click' && _this._parentMenu) {
_this._parentMenu.closed.emit(reason);
}
});
if (this.triggersSubmenu()) {
// Subscribe to changes in the hovered item in order to toggle the panel.
this._hoverSubscription = this._parentMenu._hovered()
.pipe(filter(function (active) { return active === _this._menuItemInstance; }))
.subscribe(function () {
_this._openedByMouse = true;
_this.openMenu();
});
}
};
/**
* @return {?}
*/
MatMenuTrigger.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
if (this._overlayRef) {
this._overlayRef.dispose();
this._overlayRef = null;
}
this._cleanUpSubscriptions();
};
Object.defineProperty(MatMenuTrigger.prototype, "menuOpen", {
/** Whether the menu is open. */
get: /**
* Whether the menu is open.
* @return {?}
*/
function () {
return this._menuOpen;
},
enumerable: true,
configurable: true
});
Object.defineProperty(MatMenuTrigger.prototype, "dir", {
/** The text direction of the containing app. */
get: /**
* The text direction of the containing app.
* @return {?}
*/
function () {
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
},
enumerable: true,
configurable: true
});
/** Whether the menu triggers a sub-menu or a top-level one. */
/**
* Whether the menu triggers a sub-menu or a top-level one.
* @return {?}
*/
MatMenuTrigger.prototype.triggersSubmenu = /**
* Whether the menu triggers a sub-menu or a top-level one.
* @return {?}
*/
function () {
return !!(this._menuItemInstance && this._parentMenu);
};
/** Toggles the menu between the open and closed states. */
/**
* Toggles the menu between the open and closed states.
* @return {?}
*/
MatMenuTrigger.prototype.toggleMenu = /**
* Toggles the menu between the open and closed states.
* @return {?}
*/
function () {
return this._menuOpen ? this.closeMenu() : this.openMenu();
};
/** Opens the menu. */
/**
* Opens the menu.
* @return {?}
*/
MatMenuTrigger.prototype.openMenu = /**
* Opens the menu.
* @return {?}
*/
function () {
var _this = this;
if (this._menuOpen) {
return;
}
this._createOverlay().attach(this._portal);
if (this.menu.lazyContent) {
this.menu.lazyContent.attach(this.menuData);
}
this._closeSubscription = this._menuClosingActions().subscribe(function () { return _this.closeMenu(); });
this._initMenu();
if (this.menu instanceof MatMenu) {
this.menu._startAnimation();
}
};
/** Closes the menu. */
/**
* Closes the menu.
* @return {?}
*/
MatMenuTrigger.prototype.closeMenu = /**
* Closes the menu.
* @return {?}
*/
function () {
this.menu.close.emit();
};
/**
* Focuses the menu trigger.
* @param origin Source of the menu trigger's focus.
*/
/**
* Focuses the menu trigger.
* @param {?=} origin Source of the menu trigger's focus.
* @return {?}
*/
MatMenuTrigger.prototype.focus = /**
* Focuses the menu trigger.
* @param {?=} origin Source of the menu trigger's focus.
* @return {?}
*/
function (origin) {
if (origin === void 0) { origin = 'program'; }
if (this._focusMonitor) {
this._focusMonitor.focusVia(this._element.nativeElement, origin);
}
else {
this._element.nativeElement.focus();
}
};
/**
* Closes the menu and does the necessary cleanup.
* @return {?}
*/
MatMenuTrigger.prototype._destroyMenu = /**
* Closes the menu and does the necessary cleanup.
* @return {?}
*/
function () {
if (this._overlayRef && this.menuOpen) {
this._resetMenu();
this._closeSubscription.unsubscribe();
this._overlayRef.detach();
if (this.menu instanceof MatMenu) {
this.menu._resetAnimation();
}
}
};
/**
* This method sets the menu state to open and focuses the first item if
* the menu was opened via the keyboard.
* @return {?}
*/
MatMenuTrigger.prototype._initMenu = /**
* This method sets the menu state to open and focuses the first item if
* the menu was opened via the keyboard.
* @return {?}
*/
function () {
this.menu.parentMenu = this.triggersSubmenu() ? this._parentMenu : undefined;
this.menu.direction = this.dir;
this._setMenuElevation();
this._setIsMenuOpen(true);
this.menu.focusFirstItem(this._openedByMouse ? 'mouse' : 'program');
};
/**
* Updates the menu elevation based on the amount of parent menus that it has.
* @return {?}
*/
MatMenuTrigger.prototype._setMenuElevation = /**
* Updates the menu elevation based on the amount of parent menus that it has.
* @return {?}
*/
function () {
if (this.menu.setElevation) {
var /** @type {?} */ depth = 0;
var /** @type {?} */ parentMenu = this.menu.parentMenu;
while (parentMenu) {
depth++;
parentMenu = parentMenu.parentMenu;
}
this.menu.setElevation(depth);
}
};
/**
* This method resets the menu when it's closed, most importantly restoring
* focus to the menu trigger if the menu was opened via the keyboard.
* @return {?}
*/
MatMenuTrigger.prototype._resetMenu = /**
* This method resets the menu when it's closed, most importantly restoring
* focus to the menu trigger if the menu was opened via the keyboard.
* @return {?}
*/
function () {
this._setIsMenuOpen(false);
// We should reset focus if the user is navigating using a keyboard or
// if we have a top-level trigger which might cause focus to be lost
// when clicking on the backdrop.
if (!this._openedByMouse) {
// Note that the focus style will show up both for `program` and
// `keyboard` so we don't have to specify which one it is.
this.focus();
}
else if (!this.triggersSubmenu()) {
this.focus('mouse');
}
this._openedByMouse = false;
};
/**
* @param {?} isOpen
* @return {?}
*/
MatMenuTrigger.prototype._setIsMenuOpen = /**
* @param {?} isOpen
* @return {?}
*/
function (isOpen) {
this._menuOpen = isOpen;
this._menuOpen ? this.menuOpened.emit() : this.menuClosed.emit();
if (this.triggersSubmenu()) {
this._menuItemInstance._highlighted = isOpen;
}
};
/**
* This method checks that a valid instance of MatMenu has been passed into
* matMenuTriggerFor. If not, an exception is thrown.
* @return {?}
*/
MatMenuTrigger.prototype._checkMenu = /**
* This method checks that a valid instance of MatMenu has been passed into
* matMenuTriggerFor. If not, an exception is thrown.
* @return {?}
*/
function () {
if (!this.menu) {
throwMatMenuMissingError();
}
};
/**
* This method creates the overlay from the provided menu's template and saves its
* OverlayRef so that it can be attached to the DOM when openMenu is called.
* @return {?}
*/
MatMenuTrigger.prototype._createOverlay = /**
* This method creates the overlay from the provided menu's template and saves its
* OverlayRef so that it can be attached to the DOM when openMenu is called.
* @return {?}
*/
function () {
if (!this._overlayRef) {
this._portal = new TemplatePortal(this.menu.templateRef, this._viewContainerRef);
var /** @type {?} */ config = this._getOverlayConfig();
this._subscribeToPositions(/** @type {?} */ (config.positionStrategy));
this._overlayRef = this._overlay.create(config);
}
return this._overlayRef;
};
/**
* This method builds the configuration object needed to create the overlay, the OverlayState.
* @return {?} OverlayConfig
*/
MatMenuTrigger.prototype._getOverlayConfig = /**
* This method builds the configuration object needed to create the overlay, the OverlayState.
* @return {?} OverlayConfig
*/
function () {
return new OverlayConfig({
positionStrategy: this._getPosition(),
hasBackdrop: !this.triggersSubmenu(),
backdropClass: 'cdk-overlay-transparent-backdrop',
direction: this.dir,
scrollStrategy: this._scrollStrategy()
});
};
/**
* Listens to changes in the position of the overlay and sets the correct classes
* on the menu based on the new position. This ensures the animation origin is always
* correct, even if a fallback position is used for the overlay.
* @param {?} position
* @return {?}
*/
MatMenuTrigger.prototype._subscribeToPositions = /**
* Listens to changes in the position of the overlay and sets the correct classes
* on the menu based on the new position. This ensures the animation origin is always
* correct, even if a fallback position is used for the overlay.
* @param {?} position
* @return {?}
*/
function (position) {
var _this = this;
position.onPositionChange.subscribe(function (change) {
var /** @type {?} */ posX = change.connectionPair.overlayX === 'start' ? 'after' : 'before';
var /** @type {?} */ posY = change.connectionPair.overlayY === 'top' ? 'below' : 'above';
_this.menu.setPositionClasses(posX, posY);
});
};
/**
* This method builds the position strategy for the overlay, so the menu is properly connected
* to the trigger.
* @return {?} ConnectedPositionStrategy
*/
MatMenuTrigger.prototype._getPosition = /**
* This method builds the position strategy for the overlay, so the menu is properly connected
* to the trigger.
* @return {?} ConnectedPositionStrategy
*/
function () {
var _a = this.menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'], originX = _a[0], originFallbackX = _a[1];
var _b = this.menu.yPosition === 'above' ? ['bottom', 'top'] : ['top', 'bottom'], overlayY = _b[0], overlayFallbackY = _b[1];
var _c = [overlayY, overlayFallbackY], originY = _c[0], originFallbackY = _c[1];
var _d = [originX, originFallbackX], overlayX = _d[0], overlayFallbackX = _d[1];
var /** @type {?} */ offsetY = 0;
if (this.triggersSubmenu()) {
// When the menu is a sub-menu, it should always align itself
// to the edges of the trigger, instead of overlapping it.
overlayFallbackX = originX = this.menu.xPosition === 'before' ? 'start' : 'end';
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : -MENU_PANEL_TOP_PADDING;
}
else if (!this.menu.overlapTrigger) {
originY = overlayY === 'top' ? 'bottom' : 'top';
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';
}
return this._overlay.position()
.connectedTo(this._element, { originX: originX, originY: originY }, { overlayX: overlayX, overlayY: overlayY })
.withDirection(this.dir)
.withOffsetY(offsetY)
.withFallbackPosition({ originX: originFallbackX, originY: originY }, { overlayX: overlayFallbackX, overlayY: overlayY })
.withFallbackPosition({ originX: originX, originY: originFallbackY }, { overlayX: overlayX, overlayY: overlayFallbackY }, undefined, -offsetY)
.withFallbackPosition({ originX: originFallbackX, originY: originFallbackY }, { overlayX: overlayFallbackX, overlayY: overlayFallbackY }, undefined, -offsetY);
};
/**
* Cleans up the active subscriptions.
* @return {?}
*/
MatMenuTrigger.prototype._cleanUpSubscriptions = /**
* Cleans up the active subscriptions.
* @return {?}
*/
function () {
this._closeSubscription.unsubscribe();
this._hoverSubscription.unsubscribe();
};
/**
* Returns a stream that emits whenever an action that should close the menu occurs.
* @return {?}
*/
MatMenuTrigger.prototype._menuClosingActions = /**
* Returns a stream that emits whenever an action that should close the menu occurs.
* @return {?}
*/
function () {
var _this = this;
var /** @type {?} */ backdrop = /** @type {?} */ ((this._overlayRef)).backdropClick();
var /** @type {?} */ detachments = /** @type {?} */ ((this._overlayRef)).detachments();
var /** @type {?} */ parentClose = this._parentMenu ? this._parentMenu.close : of();
var /** @type {?} */ hover = this._parentMenu ? this._parentMenu._hovered().pipe(filter(function (active) { return active !== _this._menuItemInstance; }), filter(function () { return _this._menuOpen; })) : of();
return merge(backdrop, parentClose, hover, detachments);
};
/** Handles mouse presses on the trigger. */
/**
* Handles mouse presses on the trigger.
* @param {?} event
* @return {?}
*/
MatMenuTrigger.prototype._handleMousedown = /**
* Handles mouse presses on the trigger.
* @param {?} event
* @return {?}
*/
function (event) {
if (!isFakeMousedownFromScreenReader(event)) {
this._openedByMouse = true;
// Since clicking on the trigger won't close the menu if it opens a sub-menu,
// we should prevent focus from moving onto it via click to avoid the
// highlight from lingering on the menu item.
if (this.triggersSubmenu()) {
event.preventDefault();
}
}
};
/** Handles key presses on the trigger. */
/**
* Handles key presses on the trigger.
* @param {?} event
* @return {?}
*/
MatMenuTrigger.prototype._handleKeydown = /**
* Handles key presses on the trigger.
* @param {?} event
* @return {?}
*/
function (event) {
var /** @type {?} */ keyCode = event.keyCode;
if (this.triggersSubmenu() && ((keyCode === RIGHT_ARROW && this.dir === 'ltr') ||
(keyCode === LEFT_ARROW && this.dir === 'rtl'))) {
this.openMenu();
}
};
/** Handles click events on the trigger. */
/**
* Handles click events on the trigger.
* @param {?} event
* @return {?}
*/
MatMenuTrigger.prototype._handleClick = /**
* Handles click events on the trigger.
* @param {?} event
* @return {?}
*/
function (event) {
if (this.triggersSubmenu()) {
// Stop event propagation to avoid closing the parent menu.
event.stopPropagation();
this.openMenu();
}
else {
this.toggleMenu();
}
};
MatMenuTrigger.decorators = [
{ type: Directive, args: [{
selector: "[mat-menu-trigger-for], [matMenuTriggerFor]",
host: {
'aria-haspopup': 'true',
'(mousedown)': '_handleMousedown($event)',
'(keydown)': '_handleKeydown($event)',
'(click)': '_handleClick($event)',
},
exportAs: 'matMenuTrigger'
},] },