@angular/material
Version:
Angular Material
1,246 lines (1,232 loc) • 81.2 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
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/animations'), require('@angular/cdk/portal'), require('@angular/common'), require('@angular/core'), require('rxjs'), require('@angular/cdk/a11y'), require('@angular/material/core'), require('@angular/cdk/coercion'), require('@angular/cdk/keycodes'), require('rxjs/operators'), require('@angular/cdk/bidi'), require('@angular/cdk/overlay'), require('@angular/cdk/platform')) :
typeof define === 'function' && define.amd ? define('@angular/material/menu', ['exports', '@angular/animations', '@angular/cdk/portal', '@angular/common', '@angular/core', 'rxjs', '@angular/cdk/a11y', '@angular/material/core', '@angular/cdk/coercion', '@angular/cdk/keycodes', 'rxjs/operators', '@angular/cdk/bidi', '@angular/cdk/overlay', '@angular/cdk/platform'], factory) :
(factory((global.ng = global.ng || {}, global.ng.material = global.ng.material || {}, global.ng.material.menu = {}),global.ng.animations,global.ng.cdk.portal,global.ng.common,global.ng.core,global.rxjs,global.ng.cdk.a11y,global.ng.material.core,global.ng.cdk.coercion,global.ng.cdk.keycodes,global.rxjs.operators,global.ng.cdk.bidi,global.ng.cdk.overlay,global.ng.cdk.platform));
}(this, (function (exports,animations,portal,common,core,rxjs,a11y,core$1,coercion,keycodes,operators,bidi,overlay,platform) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} 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
* \@docs-private
* @type {?}
*/
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: animations.trigger('transformMenu', [
animations.state('void', animations.style({
opacity: 0,
transform: 'scale(0.8)'
})),
animations.transition('void => enter', animations.group([
animations.query('.mat-menu-content, .mat-mdc-menu-content', animations.animate('100ms linear', animations.style({
opacity: 1
}))),
animations.animate('120ms cubic-bezier(0, 0, 0.2, 1)', animations.style({ transform: 'scale(1)' })),
])),
animations.transition('* => void', animations.animate('100ms 25ms linear', animations.style({ opacity: 0 })))
]),
/**
* This animation fades in the background color and content of the menu panel
* after its containing element is scaled in.
*/
fadeInItems: animations.trigger('fadeInItems', [
// TODO(crisbeto): this is inside the `transformMenu`
// now. Remove next time we do breaking changes.
animations.state('showing', animations.style({ opacity: 1 })),
animations.transition('void => *', [
animations.style({ opacity: 0 }),
animations.animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')
])
])
};
/**
* @deprecated
* \@breaking-change 8.0.0
* \@docs-private
* @type {?}
*/
var fadeInItems = matMenuAnimations.fadeInItems;
/**
* @deprecated
* \@breaking-change 8.0.0
* \@docs-private
* @type {?}
*/
var transformMenu = matMenuAnimations.transformMenu;
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} 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, _changeDetectorRef) {
this._template = _template;
this._componentFactoryResolver = _componentFactoryResolver;
this._appRef = _appRef;
this._injector = _injector;
this._viewContainerRef = _viewContainerRef;
this._document = _document;
this._changeDetectorRef = _changeDetectorRef;
/**
* Emits when the menu content has been attached.
*/
this._attached = new rxjs.Subject();
}
/**
* 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 portal.TemplatePortal(this._template, this._viewContainerRef);
}
this.detach();
if (!this._outlet) {
this._outlet = new portal.DomPortalOutlet(this._document.createElement('div'), this._componentFactoryResolver, this._appRef, this._injector);
}
/** @type {?} */
var element = this._template.elementRef.nativeElement;
// 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.
(/** @type {?} */ (element.parentNode)).insertBefore(this._outlet.outletElement, element);
// When `MatMenuContent` is used in an `OnPush` component, the insertion of the menu
// content via `createEmbeddedView` does not cause the content to be seen as "dirty"
// by Angular. This causes the `@ContentChildren` for menu items within the menu to
// not be updated by Angular. By explicitly marking for check here, we tell Angular that
// it needs to check for new menu items and update the `@ContentChild` in `MatMenu`.
// @breaking-change 9.0.0 Make change detector ref required
if (this._changeDetectorRef) {
this._changeDetectorRef.markForCheck();
}
this._portal.attach(this._outlet, context);
this._attached.next();
};
/**
* Detaches the content.
* @docs-private
*/
/**
* Detaches the content.
* \@docs-private
* @return {?}
*/
MatMenuContent.prototype.detach = /**
* Detaches the content.
* \@docs-private
* @return {?}
*/
function () {
if (this._portal.isAttached) {
this._portal.detach();
}
};
/**
* @return {?}
*/
MatMenuContent.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
if (this._outlet) {
this._outlet.dispose();
}
};
MatMenuContent.decorators = [
{ type: core.Directive, args: [{
selector: 'ng-template[matMenuContent]'
},] },
];
/** @nocollapse */
MatMenuContent.ctorParameters = function () { return [
{ type: core.TemplateRef },
{ type: core.ComponentFactoryResolver },
{ type: core.ApplicationRef },
{ type: core.Injector },
{ type: core.ViewContainerRef },
{ type: undefined, decorators: [{ type: core.Inject, args: [common.DOCUMENT,] }] },
{ type: core.ChangeDetectorRef }
]; };
return MatMenuContent;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} 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("matMenuTriggerFor: 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("xPosition value must be either 'before' or after'.\n Example: <mat-menu xPosition=\"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("yPosition value must be either 'above' or below'.\n Example: <mat-menu yPosition=\"above\" #menu=\"matMenu\"></mat-menu>");
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Injection token used to provide the parent menu to menu-specific components.
* \@docs-private
* @type {?}
*/
var MAT_MENU_PANEL = new core.InjectionToken('MAT_MENU_PANEL');
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
// Boilerplate for applying mixins to MatMenuItem.
/**
* \@docs-private
*/
var
// Boilerplate for applying mixins to MatMenuItem.
/**
* \@docs-private
*/
MatMenuItemBase = /** @class */ (function () {
function MatMenuItemBase() {
}
return MatMenuItemBase;
}());
/** @type {?} */
var _MatMenuItemMixinBase = core$1.mixinDisableRipple(core$1.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, _parentMenu) {
var _this =
// @breaking-change 8.0.0 make `_focusMonitor` and `document` required params.
_super.call(this) || this;
_this._elementRef = _elementRef;
_this._focusMonitor = _focusMonitor;
_this._parentMenu = _parentMenu;
/**
* ARIA role for the menu item.
*/
_this.role = 'menuitem';
/**
* Stream that emits when the menu item is hovered.
*/
_this._hovered = new rxjs.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._elementRef, false);
}
if (_parentMenu && _parentMenu.addItem) {
_parentMenu.addItem(_this);
}
_this._document = document;
return _this;
}
/** Focuses the menu item. */
/**
* Focuses the menu item.
* @param {?=} origin
* @param {?=} options
* @return {?}
*/
MatMenuItem.prototype.focus = /**
* Focuses the menu item.
* @param {?=} origin
* @param {?=} options
* @return {?}
*/
function (origin, options) {
if (origin === void 0) { origin = 'program'; }
if (this._focusMonitor) {
this._focusMonitor.focusVia(this._getHostElement(), origin, options);
}
else {
this._getHostElement().focus(options);
}
};
/**
* @return {?}
*/
MatMenuItem.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
if (this._focusMonitor) {
this._focusMonitor.stopMonitoring(this._elementRef);
}
if (this._parentMenu && this._parentMenu.removeItem) {
this._parentMenu.removeItem(this);
}
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. */
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
/**
* Prevents the default element actions if it is disabled.
* @param {?} event
* @return {?}
*/
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
MatMenuItem.prototype._checkDisabled = /**
* Prevents the default element actions if it is disabled.
* @param {?} event
* @return {?}
*/
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
function (event) {
if (this.disabled) {
event.preventDefault();
event.stopPropagation();
}
};
/** Emits to the hover stream. */
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
/**
* Emits to the hover stream.
* @return {?}
*/
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
MatMenuItem.prototype._handleMouseEnter = /**
* Emits to the hover stream.
* @return {?}
*/
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
function () {
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 () {
/** @type {?} */
var element = this._elementRef.nativeElement;
/** @type {?} */
var textNodeType = this._document ? this._document.TEXT_NODE : 3;
/** @type {?} */
var output = '';
if (element.childNodes) {
/** @type {?} */
var 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 i = 0; i < length_1; i++) {
if (element.childNodes[i].nodeType === textNodeType) {
output += element.childNodes[i].textContent;
}
}
}
return output.trim();
};
MatMenuItem.decorators = [
{ type: core.Component, args: [{selector: '[mat-menu-item]',
exportAs: 'matMenuItem',
inputs: ['disabled', 'disableRipple'],
host: {
'[attr.role]': 'role',
'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',
},
changeDetection: core.ChangeDetectionStrategy.OnPush,
encapsulation: core.ViewEncapsulation.None,
template: "<ng-content></ng-content><div class=\"mat-menu-ripple\" matRipple [matRippleDisabled]=\"disableRipple || disabled\" [matRippleTrigger]=\"_getHostElement()\"></div>",
},] },
];
/** @nocollapse */
MatMenuItem.ctorParameters = function () { return [
{ type: core.ElementRef },
{ type: undefined, decorators: [{ type: core.Inject, args: [common.DOCUMENT,] }] },
{ type: a11y.FocusMonitor },
{ type: undefined, decorators: [{ type: core.Inject, args: [MAT_MENU_PANEL,] }, { type: core.Optional }] }
]; };
MatMenuItem.propDecorators = {
role: [{ type: core.Input }],
_checkDisabled: [{ type: core.HostListener, args: ['click', ['$event'],] }],
_handleMouseEnter: [{ type: core.HostListener, args: ['mouseenter',] }]
};
return MatMenuItem;
}(_MatMenuItemMixinBase));
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Injection token to be used to override the default options for `mat-menu`.
* @type {?}
*/
var MAT_MENU_DEFAULT_OPTIONS = new core.InjectionToken('mat-menu-default-options', {
providedIn: 'root',
factory: MAT_MENU_DEFAULT_OPTIONS_FACTORY
});
/**
* \@docs-private
* @return {?}
*/
function MAT_MENU_DEFAULT_OPTIONS_FACTORY() {
return {
overlapTrigger: false,
xPosition: 'after',
yPosition: 'below',
backdropClass: 'cdk-overlay-transparent-backdrop',
};
}
/**
* Start elevation for the menu panel.
* \@docs-private
* @type {?}
*/
var MAT_MENU_BASE_ELEVATION = 4;
/**
* Base class with all of the `MatMenu` functionality.
*/
// tslint:disable-next-line:class-name
var _MatMenuBase = /** @class */ (function () {
function _MatMenuBase(_elementRef, _ngZone, _defaultOptions) {
this._elementRef = _elementRef;
this._ngZone = _ngZone;
this._defaultOptions = _defaultOptions;
this._xPosition = this._defaultOptions.xPosition;
this._yPosition = this._defaultOptions.yPosition;
/**
* Only the direct descendant menu items.
*/
this._directDescendantItems = new core.QueryList();
/**
* Subscription to tab events on the menu panel
*/
this._tabSubscription = rxjs.Subscription.EMPTY;
/**
* Config object to be passed into the menu's ngClass
*/
this._classList = {};
/**
* Current state of the panel animation.
*/
this._panelAnimationState = 'void';
/**
* Emits whenever an animation on the menu completes.
*/
this._animationDone = new rxjs.Subject();
/**
* Class to be added to the backdrop element.
*/
this.backdropClass = this._defaultOptions.backdropClass;
this._overlapTrigger = this._defaultOptions.overlapTrigger;
this._hasBackdrop = this._defaultOptions.hasBackdrop;
/**
* Event emitted when the menu is closed.
*/
this.closed = new core.EventEmitter();
/**
* Event emitted when the menu is closed.
* @deprecated Switch to `closed` instead
* \@breaking-change 8.0.0
*/
this.close = this.closed;
}
Object.defineProperty(_MatMenuBase.prototype, "xPosition", {
/** Position of the menu in the X axis. */
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(_MatMenuBase.prototype, "yPosition", {
/** Position of the menu in the Y axis. */
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(_MatMenuBase.prototype, "overlapTrigger", {
/** Whether the menu should overlap its trigger. */
get: /**
* Whether the menu should overlap its trigger.
* @return {?}
*/
function () { return this._overlapTrigger; },
set: /**
* @param {?} value
* @return {?}
*/
function (value) {
this._overlapTrigger = coercion.coerceBooleanProperty(value);
},
enumerable: true,
configurable: true
});
Object.defineProperty(_MatMenuBase.prototype, "hasBackdrop", {
/** Whether the menu has a backdrop. */
get: /**
* Whether the menu has a backdrop.
* @return {?}
*/
function () { return this._hasBackdrop; },
set: /**
* @param {?} value
* @return {?}
*/
function (value) {
this._hasBackdrop = coercion.coerceBooleanProperty(value);
},
enumerable: true,
configurable: true
});
Object.defineProperty(_MatMenuBase.prototype, "panelClass", {
/**
* 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
*/
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) {
var _this = this;
/** @type {?} */
var previousPanelClass = this._previousPanelClass;
if (previousPanelClass && previousPanelClass.length) {
previousPanelClass.split(' ').forEach((/**
* @param {?} className
* @return {?}
*/
function (className) {
_this._classList[className] = false;
}));
}
this._previousPanelClass = classes;
if (classes && classes.length) {
classes.split(' ').forEach((/**
* @param {?} className
* @return {?}
*/
function (className) {
_this._classList[className] = true;
}));
this._elementRef.nativeElement.className = '';
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(_MatMenuBase.prototype, "classList", {
/**
* 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.
* @breaking-change 8.0.0
*/
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.
* \@breaking-change 8.0.0
* @return {?}
*/
function () { return this.panelClass; },
set: /**
* @param {?} classes
* @return {?}
*/
function (classes) { this.panelClass = classes; },
enumerable: true,
configurable: true
});
/**
* @return {?}
*/
_MatMenuBase.prototype.ngOnInit = /**
* @return {?}
*/
function () {
this.setPositionClasses();
};
/**
* @return {?}
*/
_MatMenuBase.prototype.ngAfterContentInit = /**
* @return {?}
*/
function () {
var _this = this;
this._updateDirectDescendants();
this._keyManager = new a11y.FocusKeyManager(this._directDescendantItems).withWrap().withTypeAhead();
this._tabSubscription = this._keyManager.tabOut.subscribe((/**
* @return {?}
*/
function () { return _this.closed.emit('tab'); }));
};
/**
* @return {?}
*/
_MatMenuBase.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
this._directDescendantItems.destroy();
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 {?}
*/
_MatMenuBase.prototype._hovered = /**
* Stream that emits whenever the hovered menu item changes.
* @return {?}
*/
function () {
// Coerce the `changes` property because Angular types it as `Observable<any>`
/** @type {?} */
var itemChanges = (/** @type {?} */ (this._directDescendantItems.changes));
return itemChanges.pipe(operators.startWith(this._directDescendantItems), operators.switchMap((/**
* @param {?} items
* @return {?}
*/
function (items) { return rxjs.merge.apply(void 0, items.map((/**
* @param {?} item
* @return {?}
*/
function (item) { return item._hovered; }))); })));
};
/*
* Registers a menu item with the menu.
* @docs-private
* @deprecated No longer being used. To be removed.
* @breaking-change 9.0.0
*/
/*
* Registers a menu item with the menu.
* @docs-private
* @deprecated No longer being used. To be removed.
* @breaking-change 9.0.0
*/
/**
* @param {?} _item
* @return {?}
*/
_MatMenuBase.prototype.addItem = /*
* Registers a menu item with the menu.
* @docs-private
* @deprecated No longer being used. To be removed.
* @breaking-change 9.0.0
*/
/**
* @param {?} _item
* @return {?}
*/
function (_item) { };
/**
* Removes an item from the menu.
* @docs-private
* @deprecated No longer being used. To be removed.
* @breaking-change 9.0.0
*/
/**
* Removes an item from the menu.
* \@docs-private
* @deprecated No longer being used. To be removed.
* \@breaking-change 9.0.0
* @param {?} _item
* @return {?}
*/
_MatMenuBase.prototype.removeItem = /**
* Removes an item from the menu.
* \@docs-private
* @deprecated No longer being used. To be removed.
* \@breaking-change 9.0.0
* @param {?} _item
* @return {?}
*/
function (_item) { };
/** 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 {?}
*/
_MatMenuBase.prototype._handleKeydown = /**
* Handle a keyboard event from the menu, delegating to the appropriate action.
* @param {?} event
* @return {?}
*/
function (event) {
/** @type {?} */
var keyCode = event.keyCode;
/** @type {?} */
var manager = this._keyManager;
switch (keyCode) {
case keycodes.ESCAPE:
if (!keycodes.hasModifierKey(event)) {
event.preventDefault();
this.closed.emit('keydown');
}
break;
case keycodes.LEFT_ARROW:
if (this.parentMenu && this.direction === 'ltr') {
this.closed.emit('keydown');
}
break;
case keycodes.RIGHT_ARROW:
if (this.parentMenu && this.direction === 'rtl') {
this.closed.emit('keydown');
}
break;
case keycodes.HOME:
case keycodes.END:
if (!keycodes.hasModifierKey(event)) {
keyCode === keycodes.HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
event.preventDefault();
}
break;
default:
if (keyCode === keycodes.UP_ARROW || keyCode === keycodes.DOWN_ARROW) {
manager.setFocusOrigin('keyboard');
}
manager.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 {?}
*/
_MatMenuBase.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) {
if (origin === void 0) { origin = 'program'; }
/** @type {?} */
var manager = this._keyManager;
// 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(operators.take(1))
.subscribe((/**
* @return {?}
*/
function () { return manager.setFocusOrigin(origin).setFirstItemActive(); }));
}
else {
manager.setFocusOrigin(origin).setFirstItemActive();
}
// If there's no active item at this point, it means that all the items are disabled.
// Move focus to the menu panel so keyboard events like Escape still work. Also this will
// give _some_ feedback to screen readers.
if (!manager.activeItem && this._directDescendantItems.length) {
/** @type {?} */
var element = this._directDescendantItems.first._getHostElement().parentElement;
// Because the `mat-menu` is at the DOM insertion point, not inside the overlay, we don't
// have a nice way of getting a hold of the menu panel. We can't use a `ViewChild` either
// because the panel is inside an `ng-template`. We work around it by starting from one of
// the items and walking up the DOM.
while (element) {
if (element.getAttribute('role') === 'menu') {
element.focus();
break;
}
else {
element = element.parentElement;
}
}
}
};
/**
* 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 {?}
*/
_MatMenuBase.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);
};
/**
* 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 {?}
*/
_MatMenuBase.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.
/** @type {?} */
var newElevation = "mat-elevation-z" + (MAT_MENU_BASE_ELEVATION + depth);
/** @type {?} */
var customElevation = Object.keys(this._classList).find((/**
* @param {?} c
* @return {?}
*/
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;
}
};
/**
* Adds classes to the menu panel based on its position. Can be used by
* consumers to add specific styling based on the position.
* @param posX Position of the menu along the x axis.
* @param posY Position of the menu along the y axis.
* @docs-private
*/
/**
* Adds classes to the menu panel based on its position. Can be used by
* consumers to add specific styling based on the position.
* \@docs-private
* @param {?=} posX Position of the menu along the x axis.
* @param {?=} posY Position of the menu along the y axis.
* @return {?}
*/
_MatMenuBase.prototype.setPositionClasses = /**
* Adds classes to the menu panel based on its position. Can be used by
* consumers to add specific styling based on the position.
* \@docs-private
* @param {?=} posX Position of the menu along the x axis.
* @param {?=} posY Position of the menu along the y axis.
* @return {?}
*/
function (posX, posY) {
if (posX === void 0) { posX = this.xPosition; }
if (posY === void 0) { posY = this.yPosition; }
/** @type {?} */
var classes = this._classList;
classes['mat-menu-before'] = posX === 'before';
classes['mat-menu-after'] = posX === 'after';
classes['mat-menu-above'] = posY === 'above';
classes['mat-menu-below'] = posY === 'below';
};
/** Starts the enter animation. */
/**
* Starts the enter animation.
* @return {?}
*/
_MatMenuBase.prototype._startAnimation = /**
* Starts the enter animation.
* @return {?}
*/
function () {
// @breaking-change 8.0.0 Combine with _resetAnimation.
this._panelAnimationState = 'enter';
};
/** Resets the panel animation to its initial state. */
/**
* Resets the panel animation to its initial state.
* @return {?}
*/
_MatMenuBase.prototype._resetAnimation = /**
* Resets the panel animation to its initial state.
* @return {?}
*/
function () {
// @breaking-change 8.0.0 Combine with _startAnimation.
this._panelAnimationState = 'void';
};
/** Callback that is invoked when the panel animation completes. */
/**
* Callback that is invoked when the panel animation completes.
* @param {?} event
* @return {?}
*/
_MatMenuBase.prototype._onAnimationDone = /**
* Callback that is invoked when the panel animation completes.
* @param {?} event
* @return {?}
*/
function (event) {
this._animationDone.next(event);
this._isAnimating = false;
};
/**
* @param {?} event
* @return {?}
*/
_MatMenuBase.prototype._onAnimationStart = /**
* @param {?} event
* @return {?}
*/
function (event) {
this._isAnimating = true;
// Scroll the content element to the top as soon as the animation starts. This is necessary,
// because we move focus to the first item while it's still being animated, which can throw
// the browser off when it determines the scroll position. Alternatively we can move focus
// when the animation is done, however moving focus asynchronously will interrupt screen
// readers which are in the process of reading out the menu already. We take the `element`
// from the `event` since we can't use a `ViewChild` to access the pane.
if (event.toState === 'enter' && this._keyManager.activeItemIndex === 0) {
event.element.scrollTop = 0;
}
};
/**
* Sets up a stream that will keep track of any newly-added menu items and will update the list
* of direct descendants. We collect the descendants this way, because `_allItems` can include
* items that are part of child menus, and using a custom way of registering items is unreliable
* when it comes to maintaining the item order.
*/
/**
* Sets up a stream that will keep track of any newly-added menu items and will update the list
* of direct descendants. We collect the descendants this way, because `_allItems` can include
* items that are part of child menus, and using a custom way of registering items is unreliable
* when it comes to maintaining the item order.
* @private
* @return {?}
*/
_MatMenuBase.prototype._updateDirectDescendants = /**
* Sets up a stream that will keep track of any newly-added menu items and will update the list
* of direct descendants. We collect the descendants this way, because `_allItems` can include
* items that are part of child menus, and using a custom way of registering items is unreliable
* when it comes to maintaining the item order.
* @private
* @return {?}
*/
function () {
var _this = this;
this._allItems.changes
.pipe(operators.startWith(this._allItems))
.subscribe((/**
* @param {?} items
* @return {?}
*/
function (items) {
_this._directDescendantItems.reset(items.filter((/**
* @param {?} item
* @return {?}
*/
function (item) { return item._parentMenu === _this; })));
_this._directDescendantItems.notifyOnChanges();
}));
};
/** @nocollapse */
_MatMenuBase.ctorParameters = function () { return [
{ type: core.ElementRef },
{ type: core.NgZone },
{ type: undefined, decorators: [{ type: core.Inject, args: [MAT_MENU_DEFAULT_OPTIONS,] }] }
]; };
_MatMenuBase.propDecorators = {
_allItems: [{ type: core.ContentChildren, args: [MatMenuItem, { descendants: true },] }],
backdropClass: [{ type: core.Input }],
xPosition: [{ type: core.Input }],
yPosition: [{ type: core.Input }],
templateRef: [{ type: core.ViewChild, args: [core.TemplateRef, { static: false },] }],
items: [{ type: core.ContentChildren, args: [MatMenuItem,] }],
lazyContent: [{ type: core.ContentChild, args: [MatMenuContent, { static: false },] }],
overlapTrigger: [{ type: core.Input }],
hasBackdrop: [{ type: core.Input }],
panelClass: [{ type: core.Input, args: ['class',] }],
classList: [{ type: core.Input }],
closed: [{ type: core.Output }],
close: [{ type: core.Output }]
};
return _MatMenuBase;
}());
/**
* \@docs-private We show the "_MatMenu" class as "MatMenu" in the docs.
*/
var /**
* \@docs-private We show the "_MatMenu" class as "MatMenu" in the docs.
*/
MatMenu = /** @class */ (function (_super) {
__extends(MatMenu, _super);
function MatMenu() {
return _super !== null && _super.apply(this, arguments) || this;
}
return MatMenu;
}(_MatMenuBase));
// Note on the weird inheritance setup: we need three classes, because the MDC-based menu has to
// extend `MatMenu`, however keeping a reference to it will cause the inlined template and styles
// to be retained as well. The MDC menu also has to provide itself as a `MatMenu` in order for
// queries and DI to work correctly, while still not referencing the actual menu class.
// Class responsibility is split up as follows:
// * _MatMenuBase - provides all the functionality without any of the Angular metadata.
// * MatMenu - keeps the same name symbol name as the current menu and
// is used as a provider for DI and query purposes.
// * _MatMenu - the actual menu component implementation with the Angular metadata that should
// be tree shaken away for MDC.
/**
* \@docs-public MatMenu
*/
var _MatMenu = /** @class */ (function (_super) {
__extends(_MatMenu, _super);
function _MatMenu(elementRef, ngZone, defaultOptions) {
return _super.call(this, elementRef, ngZone, defaultOptions) || this;
}
_MatMenu.decorators = [
{ type: core.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.start)=\"_onAnimationStart($event)\" (@transformMenu.done)=\"_onAnimationDone($event)\" tabindex=\"-1\" role=\"menu\"><div class=\"mat-menu-content\"><ng-content></ng-content></div></div></ng-template>",
styles: [".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}@media (-ms-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: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;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}@media (-ms-high-contrast:active){.mat-menu-item-highlighted,.mat-menu-item.cdk-keyboard-focused,.mat-menu-item.cdk-program-focused{outline:dotted 1px}}.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-item .mat-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}"],
changeDetection: core.ChangeDetectionStrategy.OnPush,
encapsulation: core.ViewEncapsulation.None,
exportAs: 'matMenu',
animations: [
matMenuAnimations.transformMenu,
matMenuAnimations.fadeInItems
],
providers: [
{ provide: MAT_MENU_PANEL, useExisting: MatMenu },
{ provide: MatMenu, useExisting: _MatMenu }
]
},] },
];
/** @nocollapse */
_MatMenu.ctorParameters = function () { return [
{ type: core.ElementRef },
{ type: core.NgZone },
{ type: undefined, decorators: [{ type: core.Inject, args: [MAT_MENU_DEFAULT_OPTIONS,] }] }
]; };
return _MatMenu;
}(MatMenu));
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Injection token that determines the scroll handling while the menu is open.
* @type {?}
*/
var MAT_MENU_SCROLL_STRATEGY = new core.InjectionToken('mat-menu-scroll-strategy');
/**
* \@docs-private
* @param {?} overlay
* @return {?}
*/
function MAT_MENU_SCROLL_STRATEGY_FACTORY(overlay$$1) {
return (/**
* @return {?}
*/
function () { return overlay$$1.scrollStrategies.reposition(); });
}
/**
* \@docs-private
* @type {?}
*/
var MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER = {
provide: MAT_MENU_SCROLL_STRATEGY,
deps: [overlay.Overlay],
useFactory: MAT_MENU_SCROLL_STRATEGY_FACTORY,
};
/**
* Default top padding of the menu panel.
* @type {?}
*/
var MENU_PANEL_TOP_PADDING = 8;
/**
* Options for binding a passive event listener.
* @type {?}
*/
var passiveEventListenerOptions = platform.normalizePassiveListenerOptions({ passive: true });
// TODO(andrewseguin): Remove the kebab versions in favor of camelCased attribute selectors
/**
* 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) {
var _this = this;
this._overlay = _overlay;
this._element = _element;
this._viewContainerRef = _viewContainerRef;
this._parentMenu = _parentMenu;
this._menuItemInstance = _menuItemInstance;
this._dir = _dir;