diff --git a/src/lib/core/style/_menu-common.scss b/src/lib/core/style/_menu-common.scss index 25e87e88802f..fc96304e1791 100644 --- a/src/lib/core/style/_menu-common.scss +++ b/src/lib/core/style/_menu-common.scss @@ -68,4 +68,22 @@ $md-menu-side-padding: 16px !default; &.md-menu-before.md-menu-above { transform-origin: right bottom; } + + [dir='rtl'] & { + &.md-menu-after.md-menu-below { + transform-origin: right top; + } + + &.md-menu-after.md-menu-above { + transform-origin: right bottom; + } + + &.md-menu-before.md-menu-below { + transform-origin: left top; + } + + &.md-menu-before.md-menu-above { + transform-origin: left bottom; + } + } } \ No newline at end of file diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index 083ada1e61a1..c0bc3716e2ec 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -1,18 +1,21 @@ import { + AfterViewInit, Directive, ElementRef, + EventEmitter, Input, + OnDestroy, + Optional, Output, - EventEmitter, + Renderer, ViewContainerRef, - AfterViewInit, - OnDestroy, - Renderer } from '@angular/core'; import {MdMenuPanel} from './menu-panel'; import {MdMenuMissingError} from './menu-errors'; import { isFakeMousedownFromScreenReader, + Dir, + LayoutDirection, Overlay, OverlayState, OverlayRef, @@ -51,7 +54,8 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { @Output() onMenuClose = new EventEmitter(); constructor(private _overlay: Overlay, private _element: ElementRef, - private _viewContainerRef: ViewContainerRef, private _renderer: Renderer) {} + private _viewContainerRef: ViewContainerRef, private _renderer: Renderer, + @Optional() private _dir: Dir) {} ngAfterViewInit() { this._checkMenu(); @@ -98,6 +102,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { this._renderer.invokeElementMethod(this._element.nativeElement, 'focus'); } + /** The text direction of the containing app. */ + get dir(): LayoutDirection { + return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; + } + /** * This method ensures that the menu closes when the overlay backdrop is clicked. * We do not use first() here because doing so would not catch clicks from within @@ -173,9 +182,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { */ private _getOverlayConfig(): OverlayState { const overlayState = new OverlayState(); - overlayState.positionStrategy = this._getPosition(); + overlayState.positionStrategy = this._getPosition() + .withDirection(this.dir); overlayState.hasBackdrop = true; overlayState.backdropClass = 'md-overlay-transparent-backdrop'; + overlayState.direction = this.dir; return overlayState; } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index c6abaa6efa9d..01a6eb3219e8 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -15,9 +15,11 @@ import { MenuPositionY } from './menu'; import {OverlayContainer} from '../core/overlay/overlay-container'; +import {Dir, LayoutDirection} from '../core/rtl/dir'; describe('MdMenu', () => { let overlayContainerElement: HTMLElement; + let dir: LayoutDirection = 'ltr'; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -27,6 +29,9 @@ describe('MdMenu', () => { {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); return {getContainerElement: () => overlayContainerElement}; + }}, + {provide: Dir, useFactory: () => { + return {value: dir}; }} ] }); @@ -74,6 +79,17 @@ describe('MdMenu', () => { }).not.toThrowError(); }); + it('should set the panel direction based on the trigger direction', () => { + dir = 'rtl'; + const fixture = TestBed.createComponent(SimpleMenu); + fixture.detectChanges(); + fixture.componentInstance.trigger.openMenu(); + fixture.detectChanges(); + + const overlayPane = overlayContainerElement.children[0]; + expect(overlayPane.getAttribute('dir')).toEqual('rtl'); + }); + describe('positions', () => { it('should append md-menu-after and md-menu-below classes by default', () => { const fixture = TestBed.createComponent(SimpleMenu);