Skip to content

Commit af532df

Browse files
committed
feat(cdk/bidi): support auto direction value
Adds support for the `auto` value of the `dir` attribute. The value gets resolved to `ltr` or `rtl` based on browser's locale. Fixes #10186.
1 parent e7a3f68 commit af532df

File tree

3 files changed

+25
-13
lines changed

3 files changed

+25
-13
lines changed

src/cdk/bidi/dir.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {Directive, Output, Input, EventEmitter, AfterContentInit, OnDestroy} from '@angular/core';
1010

11-
import {Direction, Directionality} from './directionality';
11+
import {Direction, Directionality, _resolveDirectionality} from './directionality';
1212

1313
/**
1414
* Directive to listen for changes of direction of part of the DOM.
@@ -40,14 +40,16 @@ export class Dir implements Directionality, AfterContentInit, OnDestroy {
4040
get dir(): Direction {
4141
return this._dir;
4242
}
43-
set dir(value: Direction) {
44-
const old = this._dir;
45-
const normalizedValue = value ? value.toLowerCase() : value;
43+
set dir(value: Direction | 'auto') {
44+
const previousValue = this._dir;
4645

46+
// Note: `_resolveDirectionality` resolves the language based on the browser's language,
47+
// whereas the browser does it based on the content of the element. Since doing so based
48+
// on the content can be expensive, for now we're doing the simpler matching.
49+
this._dir = _resolveDirectionality(value);
4750
this._rawDir = value;
48-
this._dir = normalizedValue === 'ltr' || normalizedValue === 'rtl' ? normalizedValue : 'ltr';
4951

50-
if (old !== this._dir && this._isInitialized) {
52+
if (previousValue !== this._dir && this._isInitialized) {
5153
this.change.emit(this._dir);
5254
}
5355
}

src/cdk/bidi/directionality.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ import {DIR_DOCUMENT} from './dir-document-token';
1111

1212
export type Direction = 'ltr' | 'rtl';
1313

14+
/** Regex that matches locales with an RTL script. Taken from `goog.i18n.bidi.isRtlLanguage`. */
15+
const RTL_LOCALE_PATTERN =
16+
/^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i;
17+
18+
/** Resolves a string value to a specific direction. */
19+
export function _resolveDirectionality(rawValue: string): Direction {
20+
const value = rawValue.toLowerCase();
21+
22+
if (value === 'auto' && typeof navigator !== 'undefined' && navigator && navigator.language) {
23+
return RTL_LOCALE_PATTERN.test(navigator.language) ? 'rtl' : 'ltr';
24+
}
25+
26+
return value === 'rtl' ? 'rtl' : 'ltr';
27+
}
28+
1429
/**
1530
* The directionality (LTR / RTL) context for the application (or a subtree of it).
1631
* Exposes the current direction and a stream of direction changes.
@@ -25,14 +40,9 @@ export class Directionality implements OnDestroy {
2540

2641
constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) {
2742
if (_document) {
28-
// TODO: handle 'auto' value -
29-
// We still need to account for dir="auto".
30-
// It looks like HTMLElemenet.dir is also "auto" when that's set to the attribute,
31-
// but getComputedStyle return either "ltr" or "rtl". avoiding getComputedStyle for now
3243
const bodyDir = _document.body ? _document.body.dir : null;
3344
const htmlDir = _document.documentElement ? _document.documentElement.dir : null;
34-
const value = bodyDir || htmlDir;
35-
this.value = value === 'ltr' || value === 'rtl' ? value : 'ltr';
45+
this.value = _resolveDirectionality(bodyDir || htmlDir || 'ltr');
3646
}
3747
}
3848

tools/public_api_guard/cdk/bidi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class BidiModule {
2424
export class Dir implements Directionality, AfterContentInit, OnDestroy {
2525
readonly change: EventEmitter<Direction>;
2626
get dir(): Direction;
27-
set dir(value: Direction);
27+
set dir(value: Direction | 'auto');
2828
ngAfterContentInit(): void;
2929
// (undocumented)
3030
ngOnDestroy(): void;

0 commit comments

Comments
 (0)