chore(menu): minor file reorganization (#15850)

Reorganizes the files under `material/menu` so they make a bit more sense. The file for the `mat-menu` **component** isn't called `menu-directive` anymore. Also replaces the unnecessary `menu.ts` which was only re-exporting symbols.
This commit is contained in:
Kristiyan Kostadinov
2019-04-24 05:07:11 +02:00
committed by mmalerba
parent 3a7beb9382
commit a800c2a680
5 changed files with 427 additions and 438 deletions
-429
View File
@@ -1,429 +0,0 @@
/**
* @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 {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
import {Direction} from '@angular/cdk/bidi';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {
ESCAPE,
LEFT_ARROW,
RIGHT_ARROW,
DOWN_ARROW,
UP_ARROW,
HOME,
END,
hasModifierKey,
} from '@angular/cdk/keycodes';
import {
AfterContentInit,
ChangeDetectionStrategy,
Component,
ContentChild,
ContentChildren,
ElementRef,
EventEmitter,
Inject,
InjectionToken,
Input,
NgZone,
OnDestroy,
Output,
TemplateRef,
QueryList,
ViewChild,
ViewEncapsulation,
OnInit,
} from '@angular/core';
import {merge, Observable, Subject, Subscription} from 'rxjs';
import {startWith, switchMap, take} from 'rxjs/operators';
import {matMenuAnimations} from './menu-animations';
import {MatMenuContent} from './menu-content';
import {throwMatMenuInvalidPositionX, throwMatMenuInvalidPositionY} from './menu-errors';
import {MatMenuItem} from './menu-item';
import {MAT_MENU_PANEL, MatMenuPanel} from './menu-panel';
import {MenuPositionX, MenuPositionY} from './menu-positions';
import {AnimationEvent} from '@angular/animations';
/** Default `mat-menu` options that can be overridden. */
export interface MatMenuDefaultOptions {
/** The x-axis position of the menu. */
xPosition: MenuPositionX;
/** The y-axis position of the menu. */
yPosition: MenuPositionY;
/** Whether the menu should overlap the menu trigger. */
overlapTrigger: boolean;
/** Class to be applied to the menu's backdrop. */
backdropClass: string;
/** Whether the menu has a backdrop. */
hasBackdrop?: boolean;
}
/** Injection token to be used to override the default options for `mat-menu`. */
export const MAT_MENU_DEFAULT_OPTIONS =
new InjectionToken<MatMenuDefaultOptions>('mat-menu-default-options', {
providedIn: 'root',
factory: MAT_MENU_DEFAULT_OPTIONS_FACTORY
});
/** @docs-private */
export function MAT_MENU_DEFAULT_OPTIONS_FACTORY(): MatMenuDefaultOptions {
return {
overlapTrigger: false,
xPosition: 'after',
yPosition: 'below',
backdropClass: 'cdk-overlay-transparent-backdrop',
};
}
/**
* Start elevation for the menu panel.
* @docs-private
*/
const MAT_MENU_BASE_ELEVATION = 4;
@Component({
moduleId: module.id,
selector: 'mat-menu',
templateUrl: 'menu.html',
styleUrls: ['menu.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
exportAs: 'matMenu',
animations: [
matMenuAnimations.transformMenu,
matMenuAnimations.fadeInItems
],
providers: [
{provide: MAT_MENU_PANEL, useExisting: MatMenu}
]
})
export class MatMenu implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnInit, OnDestroy {
private _keyManager: FocusKeyManager<MatMenuItem>;
private _xPosition: MenuPositionX = this._defaultOptions.xPosition;
private _yPosition: MenuPositionY = this._defaultOptions.yPosition;
private _previousElevation: string;
/** Menu items inside the current menu. */
private _items: MatMenuItem[] = [];
/** Emits whenever the amount of menu items changes. */
private _itemChanges = new Subject<MatMenuItem[]>();
/** Subscription to tab events on the menu panel */
private _tabSubscription = Subscription.EMPTY;
/** Config object to be passed into the menu's ngClass */
_classList: {[key: string]: boolean} = {};
/** Current state of the panel animation. */
_panelAnimationState: 'void' | 'enter' = 'void';
/** Emits whenever an animation on the menu completes. */
_animationDone = new Subject<AnimationEvent>();
/** Whether the menu is animating. */
_isAnimating: boolean;
/** Parent menu of the current menu panel. */
parentMenu: MatMenuPanel | undefined;
/** Layout direction of the menu. */
direction: Direction;
/** Class to be added to the backdrop element. */
@Input() backdropClass: string = this._defaultOptions.backdropClass;
/** Position of the menu in the X axis. */
@Input()
get xPosition(): MenuPositionX { return this._xPosition; }
set xPosition(value: MenuPositionX) {
if (value !== 'before' && value !== 'after') {
throwMatMenuInvalidPositionX();
}
this._xPosition = value;
this.setPositionClasses();
}
/** Position of the menu in the Y axis. */
@Input()
get yPosition(): MenuPositionY { return this._yPosition; }
set yPosition(value: MenuPositionY) {
if (value !== 'above' && value !== 'below') {
throwMatMenuInvalidPositionY();
}
this._yPosition = value;
this.setPositionClasses();
}
/** @docs-private */
@ViewChild(TemplateRef, {static: false}) templateRef: TemplateRef<any>;
/**
* List of the items inside of a menu.
* @deprecated
* @breaking-change 8.0.0
*/
@ContentChildren(MatMenuItem) items: QueryList<MatMenuItem>;
/**
* Menu content that will be rendered lazily.
* @docs-private
*/
@ContentChild(MatMenuContent, {static: false}) lazyContent: MatMenuContent;
/** Whether the menu should overlap its trigger. */
@Input()
get overlapTrigger(): boolean { return this._overlapTrigger; }
set overlapTrigger(value: boolean) {
this._overlapTrigger = coerceBooleanProperty(value);
}
private _overlapTrigger: boolean = this._defaultOptions.overlapTrigger;
/** Whether the menu has a backdrop. */
@Input()
get hasBackdrop(): boolean | undefined { return this._hasBackdrop; }
set hasBackdrop(value: boolean | undefined) {
this._hasBackdrop = coerceBooleanProperty(value);
}
private _hasBackdrop: boolean | undefined = this._defaultOptions.hasBackdrop;
/**
* 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
*/
@Input('class')
set panelClass(classes: string) {
const previousPanelClass = this._previousPanelClass;
if (previousPanelClass && previousPanelClass.length) {
previousPanelClass.split(' ').forEach((className: string) => {
this._classList[className] = false;
});
}
this._previousPanelClass = classes;
if (classes && classes.length) {
classes.split(' ').forEach((className: string) => {
this._classList[className] = true;
});
this._elementRef.nativeElement.className = '';
}
}
private _previousPanelClass: string;
/**
* 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
*/
@Input()
get classList(): string { return this.panelClass; }
set classList(classes: string) { this.panelClass = classes; }
/** Event emitted when the menu is closed. */
@Output() readonly closed: EventEmitter<void | 'click' | 'keydown' | 'tab'> =
new EventEmitter<void | 'click' | 'keydown' | 'tab'>();
/**
* Event emitted when the menu is closed.
* @deprecated Switch to `closed` instead
* @breaking-change 8.0.0
*/
@Output() close = this.closed;
constructor(
private _elementRef: ElementRef<HTMLElement>,
private _ngZone: NgZone,
@Inject(MAT_MENU_DEFAULT_OPTIONS) private _defaultOptions: MatMenuDefaultOptions) { }
ngOnInit() {
this.setPositionClasses();
}
ngAfterContentInit() {
this._keyManager = new FocusKeyManager<MatMenuItem>(this._items).withWrap().withTypeAhead();
this._tabSubscription = this._keyManager.tabOut.subscribe(() => this.closed.emit('tab'));
}
ngOnDestroy() {
this._tabSubscription.unsubscribe();
this.closed.complete();
}
/** Stream that emits whenever the hovered menu item changes. */
_hovered(): Observable<MatMenuItem> {
return this._itemChanges.pipe(
startWith(this._items),
switchMap(items => merge(...items.map(item => item._hovered)))
);
}
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
_handleKeydown(event: KeyboardEvent) {
const keyCode = event.keyCode;
const manager = this._keyManager;
switch (keyCode) {
case ESCAPE:
this.closed.emit('keydown');
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;
case HOME:
case END:
if (!hasModifierKey(event)) {
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
event.preventDefault();
}
break;
default:
if (keyCode === UP_ARROW || keyCode === 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.
*/
focusFirstItem(origin: FocusOrigin = 'program'): void {
// 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(() => 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.
*/
resetActiveItem() {
this._keyManager.setActiveItem(-1);
}
/**
* Sets the menu panel elevation.
* @param depth Number of parent menus that come before the menu.
*/
setElevation(depth: number): void {
// The elevation starts at the base and increases by one for each level.
const newElevation = `mat-elevation-z${MAT_MENU_BASE_ELEVATION + depth}`;
const customElevation = Object.keys(this._classList).find(c => 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;
}
}
/**
* Registers a menu item with the menu.
* @docs-private
*/
addItem(item: MatMenuItem) {
// We register the items through this method, rather than picking them up through
// `ContentChildren`, because we need the items to be picked up by their closest
// `mat-menu` ancestor. If we used `@ContentChildren(MatMenuItem, {descendants: true})`,
// all descendant items will bleed into the top-level menu in the case where the consumer
// has `mat-menu` instances nested inside each other.
if (this._items.indexOf(item) === -1) {
this._items.push(item);
this._itemChanges.next(this._items);
}
}
/**
* Removes an item from the menu.
* @docs-private
*/
removeItem(item: MatMenuItem) {
const index = this._items.indexOf(item);
if (this._items.indexOf(item) > -1) {
this._items.splice(index, 1);
this._itemChanges.next(this._items);
}
}
/**
* 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
*/
setPositionClasses(posX: MenuPositionX = this.xPosition, posY: MenuPositionY = this.yPosition) {
const 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. */
_startAnimation() {
// @breaking-change 8.0.0 Combine with _resetAnimation.
this._panelAnimationState = 'enter';
}
/** Resets the panel animation to its initial state. */
_resetAnimation() {
// @breaking-change 8.0.0 Combine with _startAnimation.
this._panelAnimationState = 'void';
}
/** Callback that is invoked when the panel animation completes. */
_onAnimationDone(event: AnimationEvent) {
this._animationDone.next(event);
this._isAnimating = false;
}
_onAnimationStart(event: AnimationEvent) {
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;
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {MatCommonModule, MatRippleModule} from '@angular/material/core';
import {MatMenuContent} from './menu-content';
import {MatMenu} from './menu-directive';
import {MatMenu} from './menu';
import {MatMenuItem} from './menu-item';
import {
MatMenuTrigger,
+1 -1
View File
@@ -36,7 +36,7 @@ import {
import {normalizePassiveListenerOptions} from '@angular/cdk/platform';
import {asapScheduler, merge, of as observableOf, Subscription} from 'rxjs';
import {delay, filter, take, takeUntil} from 'rxjs/operators';
import {MatMenu} from './menu-directive';
import {MatMenu} from './menu';
import {throwMatMenuMissingError} from './menu-errors';
import {MatMenuItem} from './menu-item';
import {MatMenuPanel} from './menu-panel';
+420 -5
View File
@@ -6,8 +6,423 @@
* found in the LICENSE file at https://angular.io/license
*/
export {MatMenu, MatMenuDefaultOptions, MAT_MENU_DEFAULT_OPTIONS} from './menu-directive';
export {MatMenuItem} from './menu-item';
export {MatMenuTrigger} from './menu-trigger';
export {MatMenuPanel} from './menu-panel';
export {MenuPositionX, MenuPositionY} from './menu-positions';
import {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
import {Direction} from '@angular/cdk/bidi';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {
ESCAPE,
LEFT_ARROW,
RIGHT_ARROW,
DOWN_ARROW,
UP_ARROW,
HOME,
END,
hasModifierKey,
} from '@angular/cdk/keycodes';
import {
AfterContentInit,
ChangeDetectionStrategy,
Component,
ContentChild,
ContentChildren,
ElementRef,
EventEmitter,
Inject,
InjectionToken,
Input,
NgZone,
OnDestroy,
Output,
TemplateRef,
QueryList,
ViewChild,
ViewEncapsulation,
OnInit,
} from '@angular/core';
import {merge, Observable, Subject, Subscription} from 'rxjs';
import {startWith, switchMap, take} from 'rxjs/operators';
import {matMenuAnimations} from './menu-animations';
import {MatMenuContent} from './menu-content';
import {MenuPositionX, MenuPositionY} from './menu-positions';
import {throwMatMenuInvalidPositionX, throwMatMenuInvalidPositionY} from './menu-errors';
import {MatMenuItem} from './menu-item';
import {MAT_MENU_PANEL, MatMenuPanel} from './menu-panel';
import {AnimationEvent} from '@angular/animations';
/** Default `mat-menu` options that can be overridden. */
export interface MatMenuDefaultOptions {
/** The x-axis position of the menu. */
xPosition: MenuPositionX;
/** The y-axis position of the menu. */
yPosition: MenuPositionY;
/** Whether the menu should overlap the menu trigger. */
overlapTrigger: boolean;
/** Class to be applied to the menu's backdrop. */
backdropClass: string;
/** Whether the menu has a backdrop. */
hasBackdrop?: boolean;
}
/** Injection token to be used to override the default options for `mat-menu`. */
export const MAT_MENU_DEFAULT_OPTIONS =
new InjectionToken<MatMenuDefaultOptions>('mat-menu-default-options', {
providedIn: 'root',
factory: MAT_MENU_DEFAULT_OPTIONS_FACTORY
});
/** @docs-private */
export function MAT_MENU_DEFAULT_OPTIONS_FACTORY(): MatMenuDefaultOptions {
return {
overlapTrigger: false,
xPosition: 'after',
yPosition: 'below',
backdropClass: 'cdk-overlay-transparent-backdrop',
};
}
/**
* Start elevation for the menu panel.
* @docs-private
*/
const MAT_MENU_BASE_ELEVATION = 4;
@Component({
moduleId: module.id,
selector: 'mat-menu',
templateUrl: 'menu.html',
styleUrls: ['menu.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
exportAs: 'matMenu',
animations: [
matMenuAnimations.transformMenu,
matMenuAnimations.fadeInItems
],
providers: [
{provide: MAT_MENU_PANEL, useExisting: MatMenu}
]
})
export class MatMenu implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnInit, OnDestroy {
private _keyManager: FocusKeyManager<MatMenuItem>;
private _xPosition: MenuPositionX = this._defaultOptions.xPosition;
private _yPosition: MenuPositionY = this._defaultOptions.yPosition;
private _previousElevation: string;
/** Menu items inside the current menu. */
private _items: MatMenuItem[] = [];
/** Emits whenever the amount of menu items changes. */
private _itemChanges = new Subject<MatMenuItem[]>();
/** Subscription to tab events on the menu panel */
private _tabSubscription = Subscription.EMPTY;
/** Config object to be passed into the menu's ngClass */
_classList: {[key: string]: boolean} = {};
/** Current state of the panel animation. */
_panelAnimationState: 'void' | 'enter' = 'void';
/** Emits whenever an animation on the menu completes. */
_animationDone = new Subject<AnimationEvent>();
/** Whether the menu is animating. */
_isAnimating: boolean;
/** Parent menu of the current menu panel. */
parentMenu: MatMenuPanel | undefined;
/** Layout direction of the menu. */
direction: Direction;
/** Class to be added to the backdrop element. */
@Input() backdropClass: string = this._defaultOptions.backdropClass;
/** Position of the menu in the X axis. */
@Input()
get xPosition(): MenuPositionX { return this._xPosition; }
set xPosition(value: MenuPositionX) {
if (value !== 'before' && value !== 'after') {
throwMatMenuInvalidPositionX();
}
this._xPosition = value;
this.setPositionClasses();
}
/** Position of the menu in the Y axis. */
@Input()
get yPosition(): MenuPositionY { return this._yPosition; }
set yPosition(value: MenuPositionY) {
if (value !== 'above' && value !== 'below') {
throwMatMenuInvalidPositionY();
}
this._yPosition = value;
this.setPositionClasses();
}
/** @docs-private */
@ViewChild(TemplateRef, {static: false}) templateRef: TemplateRef<any>;
/**
* List of the items inside of a menu.
* @deprecated
* @breaking-change 8.0.0
*/
@ContentChildren(MatMenuItem) items: QueryList<MatMenuItem>;
/**
* Menu content that will be rendered lazily.
* @docs-private
*/
@ContentChild(MatMenuContent, {static: false}) lazyContent: MatMenuContent;
/** Whether the menu should overlap its trigger. */
@Input()
get overlapTrigger(): boolean { return this._overlapTrigger; }
set overlapTrigger(value: boolean) {
this._overlapTrigger = coerceBooleanProperty(value);
}
private _overlapTrigger: boolean = this._defaultOptions.overlapTrigger;
/** Whether the menu has a backdrop. */
@Input()
get hasBackdrop(): boolean | undefined { return this._hasBackdrop; }
set hasBackdrop(value: boolean | undefined) {
this._hasBackdrop = coerceBooleanProperty(value);
}
private _hasBackdrop: boolean | undefined = this._defaultOptions.hasBackdrop;
/**
* 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
*/
@Input('class')
set panelClass(classes: string) {
const previousPanelClass = this._previousPanelClass;
if (previousPanelClass && previousPanelClass.length) {
previousPanelClass.split(' ').forEach((className: string) => {
this._classList[className] = false;
});
}
this._previousPanelClass = classes;
if (classes && classes.length) {
classes.split(' ').forEach((className: string) => {
this._classList[className] = true;
});
this._elementRef.nativeElement.className = '';
}
}
private _previousPanelClass: string;
/**
* 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
*/
@Input()
get classList(): string { return this.panelClass; }
set classList(classes: string) { this.panelClass = classes; }
/** Event emitted when the menu is closed. */
@Output() readonly closed: EventEmitter<void | 'click' | 'keydown' | 'tab'> =
new EventEmitter<void | 'click' | 'keydown' | 'tab'>();
/**
* Event emitted when the menu is closed.
* @deprecated Switch to `closed` instead
* @breaking-change 8.0.0
*/
@Output() close = this.closed;
constructor(
private _elementRef: ElementRef<HTMLElement>,
private _ngZone: NgZone,
@Inject(MAT_MENU_DEFAULT_OPTIONS) private _defaultOptions: MatMenuDefaultOptions) { }
ngOnInit() {
this.setPositionClasses();
}
ngAfterContentInit() {
this._keyManager = new FocusKeyManager<MatMenuItem>(this._items).withWrap().withTypeAhead();
this._tabSubscription = this._keyManager.tabOut.subscribe(() => this.closed.emit('tab'));
}
ngOnDestroy() {
this._tabSubscription.unsubscribe();
this.closed.complete();
}
/** Stream that emits whenever the hovered menu item changes. */
_hovered(): Observable<MatMenuItem> {
return this._itemChanges.pipe(
startWith(this._items),
switchMap(items => merge(...items.map(item => item._hovered)))
);
}
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
_handleKeydown(event: KeyboardEvent) {
const keyCode = event.keyCode;
const manager = this._keyManager;
switch (keyCode) {
case ESCAPE:
this.closed.emit('keydown');
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;
case HOME:
case END:
if (!hasModifierKey(event)) {
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
event.preventDefault();
}
break;
default:
if (keyCode === UP_ARROW || keyCode === 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.
*/
focusFirstItem(origin: FocusOrigin = 'program'): void {
// 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(() => 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.
*/
resetActiveItem() {
this._keyManager.setActiveItem(-1);
}
/**
* Sets the menu panel elevation.
* @param depth Number of parent menus that come before the menu.
*/
setElevation(depth: number): void {
// The elevation starts at the base and increases by one for each level.
const newElevation = `mat-elevation-z${MAT_MENU_BASE_ELEVATION + depth}`;
const customElevation = Object.keys(this._classList).find(c => 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;
}
}
/**
* Registers a menu item with the menu.
* @docs-private
*/
addItem(item: MatMenuItem) {
// We register the items through this method, rather than picking them up through
// `ContentChildren`, because we need the items to be picked up by their closest
// `mat-menu` ancestor. If we used `@ContentChildren(MatMenuItem, {descendants: true})`,
// all descendant items will bleed into the top-level menu in the case where the consumer
// has `mat-menu` instances nested inside each other.
if (this._items.indexOf(item) === -1) {
this._items.push(item);
this._itemChanges.next(this._items);
}
}
/**
* Removes an item from the menu.
* @docs-private
*/
removeItem(item: MatMenuItem) {
const index = this._items.indexOf(item);
if (this._items.indexOf(item) > -1) {
this._items.splice(index, 1);
this._itemChanges.next(this._items);
}
}
/**
* 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
*/
setPositionClasses(posX: MenuPositionX = this.xPosition, posY: MenuPositionY = this.yPosition) {
const 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. */
_startAnimation() {
// @breaking-change 8.0.0 Combine with _resetAnimation.
this._panelAnimationState = 'enter';
}
/** Resets the panel animation to its initial state. */
_resetAnimation() {
// @breaking-change 8.0.0 Combine with _startAnimation.
this._panelAnimationState = 'void';
}
/** Callback that is invoked when the panel animation completes. */
_onAnimationDone(event: AnimationEvent) {
this._animationDone.next(event);
this._isAnimating = false;
}
_onAnimationStart(event: AnimationEvent) {
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;
}
}
}
+5 -2
View File
@@ -6,8 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
export {MatMenu, MatMenuDefaultOptions, MAT_MENU_DEFAULT_OPTIONS} from './menu';
export {MatMenuItem} from './menu-item';
export {MatMenuTrigger, MAT_MENU_SCROLL_STRATEGY} from './menu-trigger';
export {MatMenuPanel} from './menu-panel';
export * from './menu-module';
export * from './menu';
export * from './menu-animations';
export * from './menu-content';
export {MAT_MENU_SCROLL_STRATEGY} from './menu-trigger';
export * from './menu-positions';