Skip to content

Commit 5e6040d

Browse files
committed
feat(directionality): a provider to get the overall directionality
- Looks at the `html` and `body` elements for `dir` attribute and sets the Directionality service value to it - Whenever someone would try to inject Directionality - if there's a Dir directive up the dom tree it would be provided fixes #3600
1 parent 21f8899 commit 5e6040d

27 files changed

+183
-126
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {ConnectedPositionStrategy} from '../core/overlay/position/connected-posi
1717
import {Observable} from 'rxjs/Observable';
1818
import {MdOptionSelectionChange, MdOption} from '../core/option/option';
1919
import {ENTER, UP_ARROW, DOWN_ARROW} from '../core/keyboard/keycodes';
20-
import {Dir} from '../core/rtl/dir';
20+
import {Directionality} from '../core/bidi/index';
2121
import {Subscription} from 'rxjs/Subscription';
2222
import {Subject} from 'rxjs/Subject';
2323
import 'rxjs/add/observable/merge';
@@ -101,7 +101,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
101101

102102
constructor(private _element: ElementRef, private _overlay: Overlay,
103103
private _viewContainerRef: ViewContainerRef,
104-
@Optional() private _dir: Dir, private _zone: NgZone,
104+
@Optional() private _dir: Directionality, private _zone: NgZone,
105105
@Optional() @Host() private _inputContainer: MdInputContainer) {}
106106

107107
ngOnDestroy() {

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
55
import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
66
import {OverlayContainer} from '../core/overlay/overlay-container';
77
import {MdInputModule} from '../input/index';
8-
import {Dir, LayoutDirection} from '../core/rtl/dir';
8+
import {Directionality, Direction} from '../core/bidi/index';
99
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
1010
import {Subscription} from 'rxjs/Subscription';
1111
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW} from '../core/keyboard/keycodes';
@@ -22,7 +22,7 @@ import 'rxjs/add/operator/map';
2222

2323
describe('MdAutocomplete', () => {
2424
let overlayContainerElement: HTMLElement;
25-
let dir: LayoutDirection;
25+
let dir: Direction;
2626

2727
beforeEach(async(() => {
2828
dir = 'ltr';
@@ -51,7 +51,7 @@ describe('MdAutocomplete', () => {
5151

5252
return {getContainerElement: () => overlayContainerElement};
5353
}},
54-
{provide: Dir, useFactory: () => {
54+
{provide: Directionality, useFactory: () => {
5555
return {value: dir};
5656
}},
5757
{provide: ViewportRuler, useClass: FakeViewportRuler}

src/lib/core/bidi/dir.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
Directive,
3+
HostBinding,
4+
Output,
5+
Input,
6+
EventEmitter
7+
} from '@angular/core';
8+
9+
import {Direction, Directionality} from './directionality';
10+
11+
/**
12+
* Directive to listen for changes of direction of part of the DOM.
13+
*
14+
* Would provide itself in case a component looks for the Directionality service
15+
*/
16+
@Directive({
17+
selector: '[dir]',
18+
// TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got.
19+
exportAs: '$implicit',
20+
providers: [
21+
{provide: Directionality, useExisting: Dir}
22+
]
23+
})
24+
export class Dir implements Directionality {
25+
/** Layout direction of the element. */
26+
@Input('dir') _dir: Direction = 'ltr';
27+
28+
/** Event emitted when the direction changes. */
29+
@Output() change = new EventEmitter<void>();
30+
31+
/** @docs-private */
32+
@HostBinding('attr.dir')
33+
get dir(): Direction {
34+
return this._dir;
35+
}
36+
37+
set dir(v: Direction) {
38+
let old = this._dir;
39+
this._dir = v;
40+
if (old != this._dir) {
41+
this.change.emit();
42+
}
43+
}
44+
45+
/** Current layout direction of the element. */
46+
get value(): Direction { return this.dir; }
47+
set value(v: Direction) { this.dir = v; }
48+
}
49+

src/lib/core/bidi/directionality.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
EventEmitter,
3+
Injectable,
4+
ModuleWithProviders,
5+
NgModule,
6+
Optional,
7+
SkipSelf
8+
} from '@angular/core';
9+
10+
import {Dir} from './dir';
11+
12+
export type Direction = 'ltr' | 'rtl';
13+
14+
/**
15+
* The directionality (LTR / RTL) context for the application (or a subtree of it).
16+
* Exposes the current direction and a stream of direction changes.
17+
*/
18+
@Injectable()
19+
export class Directionality {
20+
value: Direction = 'ltr';
21+
change: EventEmitter<void> = new EventEmitter<void>();
22+
23+
constructor() {
24+
if (document) {
25+
// TODO: handle auto value -
26+
// We still need to account for dir="auto".
27+
// It looks like HTMLElemenet.dir is also "auto" when that's set to the attribute,
28+
// but getComputedStyle return either "ltr" or "rtl". avoiding getComputedStyle for now
29+
// though, we're already calling it for the theming check.
30+
this.value =
31+
(document.body.getAttribute('dir') ||
32+
document.documentElement.getAttribute('dir') ||
33+
'ltr') as Direction;
34+
}
35+
}
36+
}
37+
38+
@NgModule({
39+
exports: [Dir],
40+
declarations: [Dir],
41+
providers: [Directionality]
42+
})
43+
export class BidiModule {
44+
/** @deprecated */
45+
static forRoot(): ModuleWithProviders {
46+
return {
47+
ngModule: BidiModule,
48+
providers: [DIRECTIONALITY_PROVIDER]
49+
};
50+
}
51+
}
52+
53+
export const DIRECTIONALITY_PROVIDER = {
54+
// If there is already a Directionality available, use that. Otherwise, provide a new one.
55+
provide: Directionality,
56+
deps: [[new Optional(), new SkipSelf(), Directionality]],
57+
useFactory: function (parentDirectionality) {
58+
return parentDirectionality || new Directionality();
59+
}
60+
};

src/lib/core/bidi/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export {
2+
Directionality,
3+
BidiModule,
4+
DIRECTIONALITY_PROVIDER,
5+
Direction
6+
} from './directionality';
7+
export {Dir} from './dir';

src/lib/core/core.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {NgModule, ModuleWithProviders} from '@angular/core';
22
import {MdLineModule} from './line/line';
3-
import {RtlModule} from './rtl/dir';
3+
import {BidiModule} from './bidi/index';
44
import {ObserveContentModule} from './observe-content/observe-content';
55
import {MdOptionModule} from './option/option';
66
import {PortalModule} from './portal/portal-directives';
@@ -11,7 +11,7 @@ import {MdRippleModule} from './ripple/index';
1111

1212

1313
// RTL
14-
export {Dir, LayoutDirection, RtlModule} from './rtl/dir';
14+
export {Dir, Direction, Directionality, BidiModule} from './bidi/index';
1515

1616
// Mutation Observer
1717
export {ObserveContentModule, ObserveContent} from './observe-content/observe-content';
@@ -117,7 +117,7 @@ export {CompatibilityModule, NoConflictStyleCompatibilityMode} from './compatibi
117117
@NgModule({
118118
imports: [
119119
MdLineModule,
120-
RtlModule,
120+
BidiModule,
121121
MdRippleModule,
122122
ObserveContentModule,
123123
PortalModule,
@@ -128,7 +128,7 @@ export {CompatibilityModule, NoConflictStyleCompatibilityMode} from './compatibi
128128
],
129129
exports: [
130130
MdLineModule,
131-
RtlModule,
131+
BidiModule,
132132
MdRippleModule,
133133
ObserveContentModule,
134134
PortalModule,

src/lib/core/overlay/overlay-directives.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {ConnectedOverlayDirective, OverlayModule} from './overlay-directives';
55
import {OverlayContainer} from './overlay-container';
66
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
77
import {ConnectedOverlayPositionChange} from './position/connected-position';
8-
import {Dir} from '../rtl/dir';
8+
import {Directionality} from '../bidi/index';
99

1010

1111
describe('Overlay directives', () => {
@@ -22,7 +22,7 @@ describe('Overlay directives', () => {
2222
overlayContainerElement = document.createElement('div');
2323
return {getContainerElement: () => overlayContainerElement};
2424
}},
25-
{provide: Dir, useFactory: () => {
25+
{provide: Directionality, useFactory: () => {
2626
return dir = { value: 'ltr' };
2727
}}
2828
],

src/lib/core/overlay/overlay-directives.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
import {PortalModule} from '../portal/portal-directives';
2323
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
2424
import {Subscription} from 'rxjs/Subscription';
25-
import {Dir, LayoutDirection} from '../rtl/dir';
25+
import {Directionality, Direction} from '../bidi/index';
2626
import {Scrollable} from './scroll/scrollable';
2727
import {coerceBooleanProperty} from '../coercion/boolean-property';
2828

@@ -154,7 +154,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
154154
private _overlay: Overlay,
155155
templateRef: TemplateRef<any>,
156156
viewContainerRef: ViewContainerRef,
157-
@Optional() private _dir: Dir) {
157+
@Optional() private _dir: Directionality) {
158158
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
159159
}
160160

@@ -164,7 +164,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
164164
}
165165

166166
/** The element's layout direction. */
167-
get dir(): LayoutDirection {
167+
get dir(): Direction {
168168
return this._dir ? this._dir.value : 'ltr';
169169
}
170170

src/lib/core/overlay/overlay-state.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {PositionStrategy} from './position/position-strategy';
2-
import {LayoutDirection} from '../rtl/dir';
2+
import {Direction} from '../bidi/index';
33

44

55
/**
@@ -29,7 +29,7 @@ export class OverlayState {
2929
minHeight: number | string;
3030

3131
/** The direction of the text in the overlay panel. */
32-
direction: LayoutDirection = 'ltr';
32+
direction: Direction = 'ltr';
3333

3434
// TODO(jelbourn): configuration still to add
3535
// - focus trap

src/lib/core/rtl/dir.ts

Lines changed: 0 additions & 62 deletions
This file was deleted.

src/lib/grid-list/grid-list.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {MdGridTile} from './grid-tile';
1414
import {TileCoordinator} from './tile-coordinator';
1515
import {TileStyler, FitTileStyler, RatioTileStyler, FixedTileStyler} from './tile-styler';
1616
import {MdGridListColsError} from './grid-list-errors';
17-
import {Dir} from '../core';
17+
import {Directionality} from '../core';
1818
import {
1919
coerceToString,
2020
coerceToNumber,
@@ -62,7 +62,7 @@ export class MdGridList implements OnInit, AfterContentChecked {
6262
constructor(
6363
private _renderer: Renderer,
6464
private _element: ElementRef,
65-
@Optional() private _dir: Dir) {}
65+
@Optional() private _dir: Directionality) {}
6666

6767
/** Amount of columns in the grid list. */
6868
@Input()

src/lib/menu/menu-trigger.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {MdMenuPanel} from './menu-panel';
1414
import {MdMenuMissingError} from './menu-errors';
1515
import {
1616
isFakeMousedownFromScreenReader,
17-
Dir,
18-
LayoutDirection,
17+
Directionality,
18+
Direction,
1919
Overlay,
2020
OverlayState,
2121
OverlayRef,
@@ -80,7 +80,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
8080

8181
constructor(private _overlay: Overlay, private _element: ElementRef,
8282
private _viewContainerRef: ViewContainerRef, private _renderer: Renderer,
83-
@Optional() private _dir: Dir) {}
83+
@Optional() private _dir: Directionality) {}
8484

8585
ngAfterViewInit() {
8686
this._checkMenu();
@@ -132,7 +132,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
132132
}
133133

134134
/** The text direction of the containing app. */
135-
get dir(): LayoutDirection {
135+
get dir(): Direction {
136136
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
137137
}
138138

src/lib/menu/menu.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import {
1919
} from './index';
2020
import {OverlayContainer} from '../core/overlay/overlay-container';
2121
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
22-
import {Dir, LayoutDirection} from '../core/rtl/dir';
22+
import {Directionality, Direction} from '../core/bidi/index';
2323
import {extendObject} from '../core/util/object-extend';
2424

2525
describe('MdMenu', () => {
2626
let overlayContainerElement: HTMLElement;
27-
let dir: LayoutDirection;
27+
let dir: Direction;
2828

2929
beforeEach(async(() => {
3030
dir = 'ltr';
@@ -44,7 +44,7 @@ describe('MdMenu', () => {
4444
document.body.style.margin = '0';
4545
return {getContainerElement: () => overlayContainerElement};
4646
}},
47-
{provide: Dir, useFactory: () => {
47+
{provide: Directionality, useFactory: () => {
4848
return {value: dir};
4949
}},
5050
{provide: ViewportRuler, useClass: FakeViewportRuler}

0 commit comments

Comments
 (0)