diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index f019c0196799..1d9968172a1d 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -28,7 +28,7 @@ import {ConnectedPositionStrategy} from '../core/overlay/position/connected-posi import {Observable} from 'rxjs/Observable'; import {MdOptionSelectionChange, MdOption} from '../core/option/option'; import {ENTER, UP_ARROW, DOWN_ARROW, ESCAPE} from '../core/keyboard/keycodes'; -import {Dir} from '../core/rtl/dir'; +import {Directionality} from '../core/bidi/index'; import {MdInputContainer} from '../input/input-container'; import {Subscription} from 'rxjs/Subscription'; import 'rxjs/add/observable/merge'; @@ -120,8 +120,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { constructor(private _element: ElementRef, private _overlay: Overlay, private _viewContainerRef: ViewContainerRef, + private _zone: NgZone, private _changeDetectorRef: ChangeDetectorRef, - @Optional() private _dir: Dir, private _zone: NgZone, + @Optional() private _dir: Directionality, @Optional() @Host() private _inputContainer: MdInputContainer, @Optional() @Inject(DOCUMENT) private _document: any) {} diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index f3e730346d9a..f3b03b8fa090 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -19,7 +19,7 @@ import { } from './index'; import {OverlayContainer} from '../core/overlay/overlay-container'; import {MdInputModule} from '../input/index'; -import {Dir, LayoutDirection} from '../core/rtl/dir'; +import {Directionality, Direction} from '../core/bidi/index'; import {Subscription} from 'rxjs/Subscription'; import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, ESCAPE} from '../core/keyboard/keycodes'; import {MdOption} from '../core/option/option'; @@ -35,7 +35,7 @@ import 'rxjs/add/operator/map'; describe('MdAutocomplete', () => { let overlayContainerElement: HTMLElement; - let dir: LayoutDirection; + let dir: Direction; let scrolledSubject = new Subject(); beforeEach(async(() => { @@ -70,7 +70,7 @@ describe('MdAutocomplete', () => { return {getContainerElement: () => overlayContainerElement}; }}, - {provide: Dir, useFactory: () => ({value: dir})}, + {provide: Directionality, useFactory: () => ({value: dir})}, {provide: ScrollDispatcher, useFactory: () => { return {scrolled: (_delay: number, callback: () => any) => { return scrolledSubject.asObservable().subscribe(callback); diff --git a/src/lib/core/bidi/dir.ts b/src/lib/core/bidi/dir.ts new file mode 100644 index 000000000000..b25d6418b1ad --- /dev/null +++ b/src/lib/core/bidi/dir.ts @@ -0,0 +1,66 @@ +/** + * @license + * Copyright Google Inc. 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 { + Directive, + HostBinding, + Output, + Input, + EventEmitter +} from '@angular/core'; + +import {Direction, Directionality} from './directionality'; + +/** + * Directive to listen for changes of direction of part of the DOM. + * + * Would provide itself in case a component looks for the Directionality service + */ +@Directive({ + selector: '[dir]', + // TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got. + exportAs: '$implicit', + providers: [ + {provide: Directionality, useExisting: Dir} + ] +}) +export class Dir implements Directionality { + /** Layout direction of the element. */ + _dir: Direction = 'ltr'; + + /** Whether the `value` has been set to its initial value. */ + private _isInitialized: boolean = false; + + /** Event emitted when the direction changes. */ + @Output('dirChange') change = new EventEmitter(); + + /** @docs-private */ + @HostBinding('attr.dir') + @Input('dir') + get dir(): Direction { + return this._dir; + } + + set dir(v: Direction) { + let old = this._dir; + this._dir = v; + if (old !== this._dir && this._isInitialized) { + this.change.emit(); + } + } + + /** Current layout direction of the element. */ + get value(): Direction { return this.dir; } + set value(v: Direction) { this.dir = v; } + + /** Initialize once default value has been set. */ + ngAfterContentInit() { + this._isInitialized = true; + } +} + diff --git a/src/lib/core/bidi/directionality.spec.ts b/src/lib/core/bidi/directionality.spec.ts new file mode 100644 index 000000000000..e56308da090c --- /dev/null +++ b/src/lib/core/bidi/directionality.spec.ts @@ -0,0 +1,104 @@ +import {async, fakeAsync, TestBed, tick} from '@angular/core/testing'; +import {Component} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {BidiModule, Directionality, DIR_DOCUMENT} from './index'; + +describe('Directionality', () => { + let fakeDocument: FakeDocument; + + beforeEach(async(() => { + fakeDocument = {body: {}, documentElement: {}}; + + TestBed.configureTestingModule({ + imports: [BidiModule], + declarations: [ElementWithDir, InjectsDirectionality], + providers: [{provide: DIR_DOCUMENT, useFactory: () => fakeDocument}], + }).compileComponents(); + })); + + describe('Service', () => { + it('should read dir from the html element if not specified on the body', () => { + fakeDocument.documentElement.dir = 'rtl'; + + let fixture = TestBed.createComponent(InjectsDirectionality); + let testComponent = fixture.debugElement.componentInstance; + + expect(testComponent.dir.value).toBe('rtl'); + }); + + it('should read dir from the body even it is also specified on the html element', () => { + fakeDocument.documentElement.dir = 'ltr'; + fakeDocument.body.dir = 'rtl'; + + let fixture = TestBed.createComponent(InjectsDirectionality); + let testComponent = fixture.debugElement.componentInstance; + + expect(testComponent.dir.value).toBe('rtl'); + }); + + it('should default to ltr if nothing is specified on either body or the html element', () => { + let fixture = TestBed.createComponent(InjectsDirectionality); + let testComponent = fixture.debugElement.componentInstance; + + expect(testComponent.dir.value).toBe('ltr'); + }); + }); + + describe('Dir directive', () => { + it('should provide itself as Directionality', () => { + let fixture = TestBed.createComponent(ElementWithDir); + const injectedDirectionality = + fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; + + fixture.detectChanges(); + + expect(injectedDirectionality.value).toBe('rtl'); + }); + + it('should emit a change event when the value changes', fakeAsync(() => { + let fixture = TestBed.createComponent(ElementWithDir); + const injectedDirectionality = + fixture.debugElement.query(By.directive(InjectsDirectionality)).componentInstance.dir; + + fixture.detectChanges(); + + expect(injectedDirectionality.value).toBe('rtl'); + expect(fixture.componentInstance.changeCount).toBe(0); + + fixture.componentInstance.direction = 'ltr'; + + fixture.detectChanges(); + tick(); + + expect(injectedDirectionality.value).toBe('ltr'); + expect(fixture.componentInstance.changeCount).toBe(1); + })); + }); +}); + + +@Component({ + template: ` +
+ +
+ ` +}) +class ElementWithDir { + direction = 'rtl'; + changeCount = 0; +} + +/** Test component with Dir directive. */ +@Component({ + selector: 'injects-directionality', + template: `
` +}) +class InjectsDirectionality { + constructor(public dir: Directionality) { } +} + +interface FakeDocument { + documentElement: {dir?: string}; + body: {dir?: string}; +} diff --git a/src/lib/core/bidi/directionality.ts b/src/lib/core/bidi/directionality.ts new file mode 100644 index 000000000000..5ec89de17a11 --- /dev/null +++ b/src/lib/core/bidi/directionality.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright Google Inc. 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 { + EventEmitter, + Injectable, + Optional, + SkipSelf, + Inject, + InjectionToken, +} from '@angular/core'; +import {DOCUMENT} from '@angular/platform-browser'; + + +export type Direction = 'ltr' | 'rtl'; + +/** + * Injection token used to inject the document into Directionality. + * This is used so that the value can be faked in tests. + * + * We can't use the real document in tests because changing the real `dir` causes geometry-based + * tests in Safari to fail. + * + * We also can't re-provide the DOCUMENT token from platform-brower because the unit tests + * themselves use things like `querySelector` in test code. + */ +export const DIR_DOCUMENT = new InjectionToken('md-dir-doc'); + +/** + * The directionality (LTR / RTL) context for the application (or a subtree of it). + * Exposes the current direction and a stream of direction changes. + */ +@Injectable() +export class Directionality { + value: Direction = 'ltr'; + change = new EventEmitter(); + + constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) { + if (typeof _document === 'object' && !!_document) { + // TODO: handle 'auto' value - + // We still need to account for dir="auto". + // It looks like HTMLElemenet.dir is also "auto" when that's set to the attribute, + // but getComputedStyle return either "ltr" or "rtl". avoiding getComputedStyle for now + // though, we're already calling it for the theming check. + this.value = (_document.body.dir || _document.documentElement.dir || 'ltr') as Direction; + } + } +} + +export function DIRECTIONALITY_PROVIDER_FACTORY(parentDirectionality, _document) { + return parentDirectionality || new Directionality(_document); +} + +export const DIRECTIONALITY_PROVIDER = { + // If there is already a Directionality available, use that. Otherwise, provide a new one. + provide: Directionality, + deps: [[new Optional(), new SkipSelf(), Directionality], [new Optional(), DOCUMENT]], + useFactory: DIRECTIONALITY_PROVIDER_FACTORY +}; diff --git a/src/lib/core/bidi/index.ts b/src/lib/core/bidi/index.ts new file mode 100644 index 000000000000..8986092c0f85 --- /dev/null +++ b/src/lib/core/bidi/index.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google Inc. 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 {NgModule} from '@angular/core'; +import {DOCUMENT} from '@angular/platform-browser'; +import {Dir} from './dir'; +import {DIR_DOCUMENT, Directionality, DIRECTIONALITY_PROVIDER} from './directionality'; + + +export { + Directionality, + DIRECTIONALITY_PROVIDER, + DIR_DOCUMENT, + Direction, +} from './directionality'; +export {Dir} from './dir'; + +@NgModule({ + exports: [Dir], + declarations: [Dir], + providers: [ + {provide: DIR_DOCUMENT, useExisting: DOCUMENT}, + Directionality, + ] +}) +export class BidiModule { } diff --git a/src/lib/core/common-behaviors/common-module.ts b/src/lib/core/common-behaviors/common-module.ts index a33a30e4f7ba..619ab45d6785 100644 --- a/src/lib/core/common-behaviors/common-module.ts +++ b/src/lib/core/common-behaviors/common-module.ts @@ -9,6 +9,7 @@ import {NgModule, InjectionToken, Optional, Inject, isDevMode} from '@angular/core'; import {DOCUMENT} from '@angular/platform-browser'; import {CompatibilityModule} from '../compatibility/compatibility'; +import {BidiModule} from '../bidi/index'; /** Injection token that configures whether the Material sanity checks are enabled. */ @@ -22,8 +23,8 @@ export const MATERIAL_SANITY_CHECKS = new InjectionToken('md-sanity-che * This module should be imported to each top-level component module (e.g., MdTabsModule). */ @NgModule({ - imports: [CompatibilityModule], - exports: [CompatibilityModule], + imports: [CompatibilityModule, BidiModule], + exports: [CompatibilityModule, BidiModule], providers: [{ provide: MATERIAL_SANITY_CHECKS, useValue: true, }], diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts index c6d863d7c102..448376073fe8 100644 --- a/src/lib/core/core.ts +++ b/src/lib/core/core.ts @@ -8,7 +8,7 @@ import {NgModule} from '@angular/core'; import {MdLineModule} from './line/line'; -import {RtlModule} from './rtl/dir'; +import {BidiModule} from './bidi/index'; import {ObserveContentModule} from './observe-content/observe-content'; import {MdOptionModule} from './option/index'; import {PortalModule} from './portal/portal-directives'; @@ -19,7 +19,7 @@ import {MdRippleModule} from './ripple/index'; // RTL -export {Dir, LayoutDirection, RtlModule} from './rtl/dir'; +export {Dir, Direction, Directionality, BidiModule} from './bidi/index'; // Mutation Observer export {ObserveContentModule, ObserveContent} from './observe-content/observe-content'; @@ -121,7 +121,7 @@ export { @NgModule({ imports: [ MdLineModule, - RtlModule, + BidiModule, MdRippleModule, ObserveContentModule, PortalModule, @@ -132,7 +132,7 @@ export { ], exports: [ MdLineModule, - RtlModule, + BidiModule, MdRippleModule, ObserveContentModule, PortalModule, diff --git a/src/lib/core/overlay/overlay-directives.spec.ts b/src/lib/core/overlay/overlay-directives.spec.ts index 8c3440865072..c333e72b22a0 100644 --- a/src/lib/core/overlay/overlay-directives.spec.ts +++ b/src/lib/core/overlay/overlay-directives.spec.ts @@ -5,7 +5,7 @@ import {ConnectedOverlayDirective, OverlayModule, OverlayOrigin} from './overlay import {OverlayContainer} from './overlay-container'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; import {ConnectedOverlayPositionChange} from './position/connected-position'; -import {Dir} from '../rtl/dir'; +import {Directionality} from '../bidi/index'; import {dispatchKeyboardEvent} from '../testing/dispatch-events'; import {ESCAPE} from '../keyboard/keycodes'; @@ -24,7 +24,7 @@ describe('Overlay directives', () => { overlayContainerElement = document.createElement('div'); return {getContainerElement: () => overlayContainerElement}; }}, - {provide: Dir, useFactory: () => { + {provide: Directionality, useFactory: () => { return dir = { value: 'ltr' }; }} ], diff --git a/src/lib/core/overlay/overlay-directives.ts b/src/lib/core/overlay/overlay-directives.ts index a85358b5a6b1..25d2d58f309a 100644 --- a/src/lib/core/overlay/overlay-directives.ts +++ b/src/lib/core/overlay/overlay-directives.ts @@ -31,7 +31,7 @@ import { } from './position/connected-position'; import {PortalModule} from '../portal/portal-directives'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; -import {Dir, LayoutDirection} from '../rtl/dir'; +import {Directionality, Direction} from '../bidi/index'; import {Scrollable} from './scroll/scrollable'; import {ScrollStrategy} from './scroll/scroll-strategy'; import {coerceBooleanProperty} from '../coercion/boolean-property'; @@ -165,7 +165,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { private _renderer: Renderer2, templateRef: TemplateRef, viewContainerRef: ViewContainerRef, - @Optional() private _dir: Dir) { + @Optional() private _dir: Directionality) { this._templatePortal = new TemplatePortal(templateRef, viewContainerRef); } @@ -175,7 +175,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { } /** The element's layout direction. */ - get dir(): LayoutDirection { + get dir(): Direction { return this._dir ? this._dir.value : 'ltr'; } diff --git a/src/lib/core/overlay/overlay-state.ts b/src/lib/core/overlay/overlay-state.ts index 35d1fb316462..9f9a37e9696b 100644 --- a/src/lib/core/overlay/overlay-state.ts +++ b/src/lib/core/overlay/overlay-state.ts @@ -7,7 +7,7 @@ */ import {PositionStrategy} from './position/position-strategy'; -import {LayoutDirection} from '../rtl/dir'; +import {Direction} from '../bidi/index'; import {ScrollStrategy} from './scroll/scroll-strategy'; @@ -44,7 +44,7 @@ export class OverlayState { minHeight: number | string; /** The direction of the text in the overlay panel. */ - direction: LayoutDirection = 'ltr'; + direction: Direction = 'ltr'; // TODO(jelbourn): configuration still to add // - focus trap diff --git a/src/lib/core/rtl/dir.ts b/src/lib/core/rtl/dir.ts deleted file mode 100644 index caca9ed0c332..000000000000 --- a/src/lib/core/rtl/dir.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @license - * Copyright Google Inc. 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 { - NgModule, - Directive, - HostBinding, - Output, - Input, - EventEmitter -} from '@angular/core'; - -export type LayoutDirection = 'ltr' | 'rtl'; - -/** - * Directive to listen for changes of direction of part of the DOM. - * - * Applications should use this directive instead of the native attribute so that Material - * components can listen on changes of direction. - */ -@Directive({ - selector: '[dir]', - // TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got. - exportAs: '$implicit' -}) -export class Dir { - /** Layout direction of the element. */ - @Input('dir') _dir: LayoutDirection = 'ltr'; - - /** Event emitted when the direction changes. */ - @Output() dirChange = new EventEmitter(); - - /** @docs-private */ - @HostBinding('attr.dir') - get dir(): LayoutDirection { - return this._dir; - } - set dir(v: LayoutDirection) { - let old = this._dir; - this._dir = v; - if (old != this._dir) { - this.dirChange.emit(); - } - } - - /** Current layout direction of the element. */ - get value(): LayoutDirection { return this.dir; } - set value(v: LayoutDirection) { this.dir = v; } -} - - -@NgModule({ - exports: [Dir], - declarations: [Dir] -}) -export class RtlModule {} diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index ce0787ab5ade..d2cf03489f19 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -27,7 +27,7 @@ import {Overlay} from '../core/overlay/overlay'; import {OverlayRef} from '../core/overlay/overlay-ref'; import {ComponentPortal} from '../core/portal/portal'; import {OverlayState} from '../core/overlay/overlay-state'; -import {Dir} from '../core/rtl/dir'; +import {Directionality} from '../core/bidi/index'; import {MdDialog} from '../dialog/dialog'; import {MdDialogRef} from '../dialog/dialog-ref'; import {PositionStrategy} from '../core/overlay/position/position-strategy'; @@ -164,9 +164,8 @@ export class MdDatepicker implements OnDestroy { private _ngZone: NgZone, private _viewContainerRef: ViewContainerRef, @Optional() private _dateAdapter: DateAdapter, - @Optional() private _dir: Dir, + @Optional() private _dir: Directionality, @Optional() @Inject(DOCUMENT) private _document: any) { - if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } diff --git a/src/lib/dialog/dialog-config.ts b/src/lib/dialog/dialog-config.ts index bea6fd8aea30..ce22b342541f 100644 --- a/src/lib/dialog/dialog-config.ts +++ b/src/lib/dialog/dialog-config.ts @@ -7,7 +7,7 @@ */ import {ViewContainerRef} from '@angular/core'; -import {LayoutDirection} from '../core'; +import {Direction} from '../core'; /** Valid ARIA roles for a dialog element. */ export type DialogRole = 'dialog' | 'alertdialog'; @@ -61,7 +61,7 @@ export class MdDialogConfig { data?: any = null; /** Layout direction for the dialog's content. */ - direction?: LayoutDirection = 'ltr'; + direction?: Direction = 'ltr'; // TODO(jelbourn): add configuration for lifecycle hooks, ARIA labelling. } diff --git a/src/lib/grid-list/grid-list.ts b/src/lib/grid-list/grid-list.ts index 12de4deb0499..cdf7f4169380 100644 --- a/src/lib/grid-list/grid-list.ts +++ b/src/lib/grid-list/grid-list.ts @@ -21,7 +21,7 @@ import { import {MdGridTile} from './grid-tile'; import {TileCoordinator} from './tile-coordinator'; import {TileStyler, FitTileStyler, RatioTileStyler, FixedTileStyler} from './tile-styler'; -import {Dir} from '../core'; +import {Directionality} from '../core'; import { coerceToString, coerceToNumber, @@ -69,7 +69,7 @@ export class MdGridList implements OnInit, AfterContentChecked { constructor( private _renderer: Renderer2, private _element: ElementRef, - @Optional() private _dir: Dir) {} + @Optional() private _dir: Directionality) {} /** Amount of columns in the grid list. */ @Input() diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index 11de10b053e7..02e6c76a9681 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -21,8 +21,8 @@ import {MdMenuPanel} from './menu-panel'; import {throwMdMenuMissingError} from './menu-errors'; import { isFakeMousedownFromScreenReader, - Dir, - LayoutDirection, + Directionality, + Direction, Overlay, OverlayState, OverlayRef, @@ -86,7 +86,8 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { @Output() onMenuClose = new EventEmitter(); constructor(private _overlay: Overlay, private _element: ElementRef, - private _viewContainerRef: ViewContainerRef, @Optional() private _dir: Dir) { } + private _viewContainerRef: ViewContainerRef, + @Optional() private _dir: Directionality) { } ngAfterViewInit() { this._checkMenu(); @@ -138,7 +139,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { } /** The text direction of the containing app. */ - get dir(): LayoutDirection { + get dir(): Direction { return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index de9cabf903c4..021d17cc63d9 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -18,7 +18,7 @@ import { MenuPositionY } from './index'; import {OverlayContainer} from '../core/overlay/overlay-container'; -import {Dir, LayoutDirection} from '../core/rtl/dir'; +import {Directionality, Direction} from '../core/bidi/index'; import {extendObject} from '../core/util/object-extend'; import {ESCAPE} from '../core/keyboard/keycodes'; import {dispatchKeyboardEvent} from '../core/testing/dispatch-events'; @@ -26,7 +26,7 @@ import {dispatchKeyboardEvent} from '../core/testing/dispatch-events'; describe('MdMenu', () => { let overlayContainerElement: HTMLElement; - let dir: LayoutDirection; + let dir: Direction; beforeEach(async(() => { dir = 'ltr'; @@ -44,9 +44,7 @@ describe('MdMenu', () => { document.body.style.margin = '0'; return {getContainerElement: () => overlayContainerElement}; }}, - {provide: Dir, useFactory: () => { - return {value: dir}; - }} + {provide: Directionality, useFactory: () => ({value: dir})} ] }); diff --git a/src/lib/module.ts b/src/lib/module.ts index eb3fa6ccc85f..f25636506fe7 100644 --- a/src/lib/module.ts +++ b/src/lib/module.ts @@ -12,10 +12,10 @@ import { A11yModule, MdCommonModule, MdRippleModule, + BidiModule, ObserveContentModule, OverlayModule, - PortalModule, - RtlModule + PortalModule } from './core/index'; import {MdButtonToggleModule} from './button-toggle/index'; @@ -76,7 +76,7 @@ const MATERIAL_MODULES = [ MdTooltipModule, OverlayModule, PortalModule, - RtlModule, + BidiModule, StyleModule, A11yModule, PlatformModule, diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 626e02bf0ad4..18dd6a82bad6 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -15,7 +15,7 @@ import {OverlayContainer} from '../core/overlay/overlay-container'; import {MdSelect} from './select'; import {getMdSelectDynamicMultipleError, getMdSelectNonArrayValueError} from './select-errors'; import {MdOption} from '../core/option/option'; -import {Dir} from '../core/rtl/dir'; +import {Directionality} from '../core/bidi/index'; import {DOWN_ARROW, UP_ARROW, ENTER, SPACE, HOME, END, TAB} from '../core/keyboard/keycodes'; import { ControlValueAccessor, FormControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule @@ -78,7 +78,7 @@ describe('MdSelect', () => { return {getContainerElement: () => overlayContainerElement}; }}, - {provide: Dir, useFactory: () => dir = { value: 'ltr' }}, + {provide: Directionality, useFactory: () => dir = { value: 'ltr' }}, {provide: ScrollDispatcher, useFactory: () => { return {scrolled: (_delay: number, callback: () => any) => { return scrolledSubject.asObservable().subscribe(callback); diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index e0cc93ee342c..4f1ccce379f6 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -29,7 +29,7 @@ import { import {MdOption, MdOptionSelectionChange, MdOptgroup} from '../core/option/index'; import {ENTER, SPACE, UP_ARROW, DOWN_ARROW, HOME, END} from '../core/keyboard/keycodes'; import {FocusKeyManager} from '../core/a11y/focus-key-manager'; -import {Dir} from '../core/rtl/dir'; +import {Directionality} from '../core/bidi/index'; import {Observable} from 'rxjs/Observable'; import {Subscription} from 'rxjs/Subscription'; import {transformPlaceholder, transformPanel, fadeInContent} from './select-animations'; @@ -322,7 +322,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On private _changeDetectorRef: ChangeDetectorRef, renderer: Renderer2, elementRef: ElementRef, - @Optional() private _dir: Dir, + @Optional() private _dir: Directionality, @Self() @Optional() public _control: NgControl, @Attribute('tabindex') tabIndex: string, @Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions) { diff --git a/src/lib/sidenav/sidenav.ts b/src/lib/sidenav/sidenav.ts index 2c21c99ca789..39a92397995d 100644 --- a/src/lib/sidenav/sidenav.ts +++ b/src/lib/sidenav/sidenav.ts @@ -22,7 +22,7 @@ import { NgZone, OnDestroy, Inject, } from '@angular/core'; -import {Dir, coerceBooleanProperty} from '../core'; +import {Directionality, coerceBooleanProperty} from '../core'; import {FocusTrapFactory, FocusTrap} from '../core/a11y/focus-trap'; import {ESCAPE} from '../core/keyboard/keycodes'; import 'rxjs/add/operator/first'; @@ -365,12 +365,12 @@ export class MdSidenavContainer implements AfterContentInit { /** Whether to enable open/close trantions. */ _enableTransitions = false; - constructor(@Optional() private _dir: Dir, private _element: ElementRef, + constructor(@Optional() private _dir: Directionality, private _element: ElementRef, private _renderer: Renderer2, private _ngZone: NgZone) { // If a `Dir` directive exists up the tree, listen direction changes and update the left/right // properties to point to the proper start/end. if (_dir != null) { - _dir.dirChange.subscribe(() => this._validateDrawers()); + _dir.change.subscribe(() => this._validateDrawers()); } } diff --git a/src/lib/slider/index.ts b/src/lib/slider/index.ts index 118e58930505..834456716c50 100644 --- a/src/lib/slider/index.ts +++ b/src/lib/slider/index.ts @@ -12,11 +12,11 @@ import {CommonModule} from '@angular/common'; import {FormsModule} from '@angular/forms'; import {MdCommonModule, GestureConfig, StyleModule} from '../core'; import {MdSlider} from './slider'; -import {RtlModule} from '../core/rtl/dir'; +import {BidiModule} from '../core/bidi/index'; @NgModule({ - imports: [CommonModule, FormsModule, MdCommonModule, StyleModule, RtlModule], + imports: [CommonModule, FormsModule, MdCommonModule, StyleModule, BidiModule], exports: [MdSlider, MdCommonModule], declarations: [MdSlider], providers: [{provide: HAMMER_GESTURE_CONFIG, useClass: GestureConfig}] diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index 116a55687b23..43d4ba28a8eb 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -4,7 +4,7 @@ import {Component, DebugElement} from '@angular/core'; import {By, HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdSlider, MdSliderModule} from './index'; import {TestGestureConfig} from './test-gesture-config'; -import {RtlModule} from '../core/rtl/dir'; +import {BidiModule} from '../core/bidi/index'; import { DOWN_ARROW, END, @@ -23,7 +23,7 @@ describe('MdSlider', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MdSliderModule, ReactiveFormsModule, FormsModule, RtlModule], + imports: [MdSliderModule, ReactiveFormsModule, FormsModule, BidiModule], declarations: [ StandardSlider, DisabledSlider, diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index f15cab396b06..4ea3363fdce8 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -20,7 +20,7 @@ import { } from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {coerceBooleanProperty, coerceNumberProperty, HammerInput} from '../core'; -import {Dir} from '../core/rtl/dir'; +import {Directionality} from '../core/bidi/index'; import { DOWN_ARROW, END, @@ -390,7 +390,8 @@ export class MdSlider extends _MdSliderMixinBase } constructor(renderer: Renderer2, private _elementRef: ElementRef, - private _focusOriginMonitor: FocusOriginMonitor, @Optional() private _dir: Dir) { + private _focusOriginMonitor: FocusOriginMonitor, + @Optional() private _dir: Directionality) { super(); this._focusOriginMonitor.monitor(this._elementRef.nativeElement, renderer, true) .subscribe((origin: FocusOrigin) => this._isActive = !!origin && origin !== 'keyboard'); diff --git a/src/lib/snack-bar/snack-bar-config.ts b/src/lib/snack-bar/snack-bar-config.ts index 868462cb5e2b..a058949b9dfa 100644 --- a/src/lib/snack-bar/snack-bar-config.ts +++ b/src/lib/snack-bar/snack-bar-config.ts @@ -7,7 +7,7 @@ */ import {ViewContainerRef} from '@angular/core'; -import {AriaLivePoliteness, LayoutDirection} from '../core'; +import {AriaLivePoliteness, Direction} from '../core'; /** * Configuration used when opening a snack-bar. @@ -29,5 +29,5 @@ export class MdSnackBarConfig { extraClasses?: string[]; /** Text layout direction for the snack bar. */ - direction?: LayoutDirection = 'ltr'; + direction?: Direction = 'ltr'; } diff --git a/src/lib/tabs/tab-body.spec.ts b/src/lib/tabs/tab-body.spec.ts index 880b32470882..9c0f7e0023b2 100644 --- a/src/lib/tabs/tab-body.spec.ts +++ b/src/lib/tabs/tab-body.spec.ts @@ -1,7 +1,7 @@ import {async, ComponentFixture, TestBed, flushMicrotasks, fakeAsync} from '@angular/core/testing'; import {Component, ViewChild, TemplateRef, ViewContainerRef} from '@angular/core'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {LayoutDirection, Dir} from '../core/rtl/dir'; +import {Direction, Directionality} from '../core/bidi/index'; import {TemplatePortal} from '../core/portal/portal'; import {MdTabBody} from './tab-body'; import {MdRippleModule} from '../core/ripple/index'; @@ -10,7 +10,7 @@ import {PortalModule} from '../core'; describe('MdTabBody', () => { - let dir: LayoutDirection = 'ltr'; + let dir: Direction = 'ltr'; beforeEach(async(() => { dir = 'ltr'; @@ -21,8 +21,8 @@ describe('MdTabBody', () => { SimpleTabBodyApp, ], providers: [ - { provide: Dir, useFactory: () => { return {value: dir}; } - }] + {provide: Directionality, useFactory: () => ({value: dir})} + ] }); TestBed.compileComponents(); diff --git a/src/lib/tabs/tab-body.ts b/src/lib/tabs/tab-body.ts index 156b877e3fae..9fc18ff700e9 100644 --- a/src/lib/tabs/tab-body.ts +++ b/src/lib/tabs/tab-body.ts @@ -26,7 +26,7 @@ import { transition, AnimationEvent, } from '@angular/animations'; -import {TemplatePortal, PortalHostDirective, Dir, LayoutDirection} from '../core'; +import {TemplatePortal, PortalHostDirective, Directionality, Direction} from '../core'; import 'rxjs/add/operator/map'; /** @@ -124,7 +124,8 @@ export class MdTabBody implements OnInit, AfterViewChecked { } } - constructor(@Optional() private _dir: Dir, private _elementRef: ElementRef) { } + constructor(private _elementRef: ElementRef, + @Optional() private _dir: Directionality) { } /** * After initialized, check if the content is centered and has an origin. If so, set the @@ -165,7 +166,7 @@ export class MdTabBody implements OnInit, AfterViewChecked { } /** The text direction of the containing app. */ - _getLayoutDirection(): LayoutDirection { + _getLayoutDirection(): Direction { return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; } diff --git a/src/lib/tabs/tab-header.spec.ts b/src/lib/tabs/tab-header.spec.ts index 308ab011b242..731b6c4ccade 100644 --- a/src/lib/tabs/tab-header.spec.ts +++ b/src/lib/tabs/tab-header.spec.ts @@ -1,6 +1,6 @@ import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; import {Component, ViewChild, ViewContainerRef} from '@angular/core'; -import {LayoutDirection, Dir} from '../core/rtl/dir'; +import {Direction, Directionality} from '../core/bidi/index'; import {MdTabHeader} from './tab-header'; import {MdRippleModule} from '../core/ripple/index'; import {CommonModule} from '@angular/common'; @@ -17,8 +17,8 @@ import {By} from '@angular/platform-browser'; describe('MdTabHeader', () => { - let dir: LayoutDirection = 'ltr'; - let dirChange = new Subject(); + let dir: Direction = 'ltr'; + let change = new Subject(); let fixture: ComponentFixture; let appComponent: SimpleTabHeaderApp; @@ -33,9 +33,7 @@ describe('MdTabHeader', () => { SimpleTabHeaderApp, ], providers: [ - {provide: Dir, useFactory: () => { - return {value: dir, dirChange: dirChange.asObservable()}; - }}, + {provide: Directionality, useFactory: () => ({value: dir, change: change.asObservable()})}, {provide: ViewportRuler, useClass: FakeViewportRuler}, ] }); @@ -239,13 +237,13 @@ describe('MdTabHeader', () => { it('should re-align the ink bar when the direction changes', () => { fixture = TestBed.createComponent(SimpleTabHeaderApp); - fixture.detectChanges(); const inkBar = fixture.componentInstance.mdTabHeader._inkBar; - spyOn(inkBar, 'alignToElement'); - dirChange.next(); + fixture.detectChanges(); + + change.next(); fixture.detectChanges(); expect(inkBar.alignToElement).toHaveBeenCalled(); @@ -301,7 +299,7 @@ class SimpleTabHeaderApp { focusedIndex: number; disabledTabIndex = 1; tabs: Tab[] = [{label: 'tab one'}, {label: 'tab one'}, {label: 'tab one'}, {label: 'tab one'}]; - dir: LayoutDirection = 'ltr'; + dir: Direction = 'ltr'; @ViewChild(MdTabHeader) mdTabHeader: MdTabHeader; diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index 410f6559b8c1..c7a9bbfcd5bb 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -22,7 +22,14 @@ import { OnDestroy, NgZone, } from '@angular/core'; -import {RIGHT_ARROW, LEFT_ARROW, ENTER, Dir, LayoutDirection, coerceBooleanProperty} from '../core'; +import { + RIGHT_ARROW, + LEFT_ARROW, + ENTER, + Directionality, + Direction, + coerceBooleanProperty +} from '../core'; import {MdTabLabelWrapper} from './tab-label-wrapper'; import {MdInkBar} from './ink-bar'; import {Subscription} from 'rxjs/Subscription'; @@ -131,7 +138,7 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit, OnDes constructor( private _elementRef: ElementRef, private _ngZone: NgZone, - @Optional() private _dir: Dir) { } + @Optional() private _dir: Directionality) { } ngAfterContentChecked(): void { // If the number of tab labels have changed, check if scrolling should be enabled @@ -176,7 +183,7 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit, OnDes */ ngAfterContentInit() { this._realignInkBar = this._ngZone.runOutsideAngular(() => { - let dirChange = this._dir ? this._dir.dirChange : Observable.of(null); + let dirChange = this._dir ? this._dir.change : Observable.of(null); let resize = typeof window !== 'undefined' ? Observable.fromEvent(window, 'resize').auditTime(10) : Observable.of(null); @@ -288,7 +295,7 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit, OnDes } /** The layout direction of the containing app. */ - _getLayoutDirection(): LayoutDirection { + _getLayoutDirection(): Direction { return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; } diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts index fbd96fc1d269..dda7ef91e2c5 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts @@ -6,12 +6,12 @@ import {By} from '@angular/platform-browser'; import {ViewportRuler} from '../../core/overlay/position/viewport-ruler'; import {FakeViewportRuler} from '../../core/overlay/position/fake-viewport-ruler'; import {dispatchFakeEvent, dispatchMouseEvent} from '../../core/testing/dispatch-events'; -import {Dir, LayoutDirection} from '../../core/rtl/dir'; +import {Direction, Directionality} from '../../core/bidi/index'; import {Subject} from 'rxjs/Subject'; describe('MdTabNavBar', () => { - let dir: LayoutDirection = 'ltr'; + let dir: Direction = 'ltr'; let dirChange = new Subject(); beforeEach(async(() => { @@ -22,9 +22,10 @@ describe('MdTabNavBar', () => { TabLinkWithNgIf, ], providers: [ - {provide: Dir, useFactory: () => { - return {value: dir, dirChange: dirChange.asObservable()}; - }}, + {provide: Directionality, useFactory: () => ({ + value: dir, + change: dirChange.asObservable() + })}, {provide: ViewportRuler, useClass: FakeViewportRuler}, ] }); diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts index 0415695ff81a..34689baceacc 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts @@ -22,7 +22,7 @@ import { import {MdInkBar} from '../ink-bar'; import {MdRipple} from '../../core/ripple/index'; import {ViewportRuler} from '../../core/overlay/position/viewport-ruler'; -import {Dir, MD_RIPPLE_GLOBAL_OPTIONS, Platform, RippleGlobalOptions} from '../../core'; +import {Directionality, MD_RIPPLE_GLOBAL_OPTIONS, Platform, RippleGlobalOptions} from '../../core'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/auditTime'; import 'rxjs/add/operator/takeUntil'; @@ -51,7 +51,7 @@ export class MdTabNav implements AfterContentInit, OnDestroy { @ViewChild(MdInkBar) _inkBar: MdInkBar; - constructor(@Optional() private _dir: Dir, private _ngZone: NgZone) { } + constructor(@Optional() private _dir: Directionality, private _ngZone: NgZone) { } /** Notifies the component that the active link has been changed. */ updateActiveLink(element: ElementRef) { @@ -61,7 +61,7 @@ export class MdTabNav implements AfterContentInit, OnDestroy { ngAfterContentInit(): void { this._ngZone.runOutsideAngular(() => { - let dirChange = this._dir ? this._dir.dirChange : Observable.of(null); + let dirChange = this._dir ? this._dir.change : Observable.of(null); let resize = typeof window !== 'undefined' ? Observable.fromEvent(window, 'resize').auditTime(10) : Observable.of(null); diff --git a/src/lib/tooltip/tooltip.spec.ts b/src/lib/tooltip/tooltip.spec.ts index 2a521f818eec..61de0c553e1a 100644 --- a/src/lib/tooltip/tooltip.spec.ts +++ b/src/lib/tooltip/tooltip.spec.ts @@ -17,7 +17,7 @@ import {By} from '@angular/platform-browser'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {TooltipPosition, MdTooltip, MdTooltipModule, SCROLL_THROTTLE_MS} from './index'; import {OverlayContainer} from '../core'; -import {Dir, LayoutDirection} from '../core/rtl/dir'; +import {Directionality, Direction} from '../core/bidi/index'; import {OverlayModule} from '../core/overlay/overlay-directives'; import {Platform} from '../core/platform/platform'; import {Scrollable} from '../core/overlay/scroll/scrollable'; @@ -28,7 +28,7 @@ const initialTooltipMessage = 'initial tooltip message'; describe('MdTooltip', () => { let overlayContainerElement: HTMLElement; - let dir: {value: LayoutDirection}; + let dir: {value: Direction}; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -41,7 +41,7 @@ describe('MdTooltip', () => { document.body.appendChild(overlayContainerElement); return {getContainerElement: () => overlayContainerElement}; }}, - {provide: Dir, useFactory: () => { + {provide: Directionality, useFactory: () => { return dir = { value: 'ltr' }; }} ] diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts index 4c73a69c51d1..aad2a9e85ee1 100644 --- a/src/lib/tooltip/tooltip.ts +++ b/src/lib/tooltip/tooltip.ts @@ -37,7 +37,7 @@ import { } from '../core'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; -import {Dir} from '../core/rtl/dir'; +import {Directionality} from '../core/bidi/index'; import {Platform} from '../core/platform/index'; import 'rxjs/add/operator/first'; import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher'; @@ -180,7 +180,7 @@ export class MdTooltip implements OnDestroy { private _ngZone: NgZone, private _renderer: Renderer2, private _platform: Platform, - @Optional() private _dir: Dir) { + @Optional() private _dir: Directionality) { // The mouse events shouldn't be bound on iOS devices, because // they can prevent the first tap from firing its click event. @@ -402,7 +402,8 @@ export class TooltipComponent { /** Subject for notifying that the tooltip has been hidden from the view */ private _onHide: Subject = new Subject(); - constructor(@Optional() private _dir: Dir, private _changeDetectorRef: ChangeDetectorRef) {} + constructor(@Optional() private _dir: Directionality, + private _changeDetectorRef: ChangeDetectorRef) {} /** * Shows the tooltip with an animation originating from the provided origin