Skip to content

Commit

Permalink
fix: apply correct RTL margins
Browse files Browse the repository at this point in the history
Copy bidi module from angular material cdk so there's not a new
dependency.
  • Loading branch information
atscott authored and ThomasBurleson committed Feb 13, 2018
1 parent 843a68d commit 7699957
Show file tree
Hide file tree
Showing 14 changed files with 458 additions and 8 deletions.
5 changes: 4 additions & 1 deletion src/demo-app/app/docs-layout/_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {Component} from '@angular/core';
<demo-flex-attribute-values class="small-demo"></demo-flex-attribute-values>
<demo-flex-offset-values class="small-demo"></demo-flex-offset-values>
<demo-flex-align-self class="small-demo"></demo-flex-align-self>
<demo-layout-row-direction class="small-demo"></demo-layout-row-direction>
`
})
export class DemosLayoutAPI {
Expand All @@ -26,6 +27,7 @@ import {DemoFlexRowFillWrap} from './flexRowFillWrap.demo';
import {DemoFlexAttributeValues} from './flexOtherValues.demo';
import {DemoFlexOffsetValues} from './flexOffetValues.demo';
import {DemoFlexAlignSelf} from './FlexAlignSelf.demo';
import {DemoLayoutRowDirection} from './layoutRowWithDirection.demo';

@NgModule({
declarations: [
Expand All @@ -37,7 +39,8 @@ import {DemoFlexAlignSelf} from './FlexAlignSelf.demo';
DemoFlexRowFillWrap,
DemoFlexAttributeValues,
DemoFlexOffsetValues,
DemoFlexAlignSelf
DemoFlexAlignSelf,
DemoLayoutRowDirection,
],
imports: [
SharedModule,
Expand Down
42 changes: 42 additions & 0 deletions src/demo-app/app/docs-layout/layoutRowWithDirection.demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {Component} from '@angular/core';

@Component({
moduleId: module.id,
selector: 'demo-layout-row-direction',
template: `
<mat-card class="card-demo">
<mat-card-title>Direction support for RTL</mat-card-title>
<mat-card-subtitle>
Simple row using layout gap and flex offset to demonstrate changes
in layout direction between rtl and ltr.
</mat-card-subtitle>
<mat-card-content fxLayout="column" fxLayoutGap="8px">
<div>
<button (click)="toggleDirection()" mat-raised-button>
Toggle direction
</button>
</div>
<div class="containerX">
<div fxLayout="row" class="colored box" fxLayoutGap="20px"
[dir]="direction" fxFlex>
<div fxFlexOffset="20px">item 1</div>
<div>item 2</div>
<div>item 3</div>
</div>
</div>
</mat-card-content>
<mat-card-footer>
<div class="hint">
&lt;div dir="{{ direction }}" fxLayoutGap="20px"&gt;
</div>
</mat-card-footer>
</mat-card>
`
})
export class DemoLayoutRowDirection {
direction = 'ltr';

toggleDirection() {
this.direction = this.direction === 'ltr' ? 'rtl' : 'ltr';
}
}
44 changes: 43 additions & 1 deletion src/lib/api/flexbox/flex-offset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {Component} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ComponentFixture, TestBed} from '@angular/core/testing';

import {DIR_DOCUMENT} from '../../bidi/directionality';
import {DEFAULT_BREAKPOINTS_PROVIDER} from '../../media-query/breakpoints/break-points-provider';
import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry';
import {MockMatchMedia} from '../../media-query/mock/mock-match-media';
Expand All @@ -27,20 +28,23 @@ import {
describe('flex directive', () => {
let fixture: ComponentFixture<any>;
let expectDOMFrom = makeExpectDOMFrom(() => TestFlexComponent);
let fakeDocument: {body: {dir?: string}, documentElement: {dir?: string}};
let componentWithTemplate = (template: string) => {
fixture = makeCreateTestComponent(() => TestFlexComponent)(template);
};

beforeEach(() => {
jasmine.addMatchers(customMatchers);
fakeDocument = {body: {}, documentElement: {}};

// Configure testbed to prepare services
TestBed.configureTestingModule({
imports: [CommonModule, FlexLayoutModule],
declarations: [TestFlexComponent],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
{provide: DIR_DOCUMENT, useValue: fakeDocument}
]
});
});
Expand Down Expand Up @@ -134,6 +138,44 @@ describe('flex directive', () => {
});
});

it('should set margin-right for rtl layouts on document body', () => {
fakeDocument.body.dir = 'rtl';
componentWithTemplate(`
<div fxLayout='row' class='test'>
<div fxFlex='30px' fxFlexOffset='17px'> </div>
</div>
`);
fixture.detectChanges();

let element = queryFor(fixture, '[fxFlex]')[0].nativeElement;
expect(element).toHaveStyle({'margin-right': '17px'});
});

it('should set margin-right for rtl layouts on documentElement', () => {
fakeDocument.documentElement.dir = 'rtl';
componentWithTemplate(`
<div fxLayout='row' class='test'>
<div fxFlex='30px' fxFlexOffset='17px'> </div>
</div>
`);
fixture.detectChanges();

let element = queryFor(fixture, '[fxFlex]')[0].nativeElement;
expect(element).toHaveStyle({'margin-right': '17px'});
});

it('should set margin-left for ltr layouts', () => {
componentWithTemplate(`
<div fxLayout='row' class='test'>
<div fxFlex='30px' fxFlexOffset='17px'> </div>
</div>
`);
fixture.detectChanges();

let element = queryFor(fixture, '[fxFlex]')[0].nativeElement;
expect(element).toHaveStyle({'margin-left': '17px'});
});

});

});
Expand Down
15 changes: 13 additions & 2 deletions src/lib/api/flexbox/flex-offset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import {Subscription} from 'rxjs/Subscription';

import {BaseFxDirective} from '../core/base';
import {Directionality} from '../../bidi/directionality';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {LayoutDirective} from './layout';
Expand All @@ -39,6 +40,7 @@ import {isFlowHorizontal} from '../../utils/layout-validator';
[fxFlexOffset.gt-xs], [fxFlexOffset.gt-sm], [fxFlexOffset.gt-md], [fxFlexOffset.gt-lg]
`})
export class FlexOffsetDirective extends BaseFxDirective implements OnInit, OnChanges, OnDestroy {
private _directionWatcher: Subscription;

/* tslint:disable */
@Input('fxFlexOffset') set offset(val) { this._cacheInput('offset', val); }
Expand All @@ -63,8 +65,11 @@ export class FlexOffsetDirective extends BaseFxDirective implements OnInit, OnCh
elRef: ElementRef,
renderer: Renderer2,
@Optional() @SkipSelf() protected _container: LayoutDirective,
@Inject(PLATFORM_ID) platformId: Object) {
@Inject(PLATFORM_ID) platformId: Object,
private _directionality: Directionality) {
super(monitor, elRef, renderer, platformId);
this._directionWatcher =
this._directionality.change.subscribe(this._updateWithValue.bind(this));


this.watchParentFlow();
Expand All @@ -91,6 +96,9 @@ export class FlexOffsetDirective extends BaseFxDirective implements OnInit, OnCh
if (this._layoutWatcher) {
this._layoutWatcher.unsubscribe();
}
if (this._directionWatcher) {
this._directionWatcher.unsubscribe();
}
}

/**
Expand Down Expand Up @@ -162,8 +170,11 @@ export class FlexOffsetDirective extends BaseFxDirective implements OnInit, OnCh
offset = offset + '%';
}

const horizontalLayoutKey =
this._directionality.value === 'rtl' ? 'margin-right' : 'margin-left';
// The flex-direction of this element's flex container. Defaults to 'row'.
let layout = this._getFlowDirection(this.parentElement, true);
return isFlowHorizontal(layout) ? {'margin-left': `${offset}`} : {'margin-top': `${offset}`};
return isFlowHorizontal(layout) ? {[horizontalLayoutKey]: `${offset}`} :
{'margin-top': `${offset}`};
}
}
23 changes: 22 additions & 1 deletion src/lib/api/flexbox/layout-gap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {Component, OnInit} from '@angular/core';
import {CommonModule} from '@angular/common';
import {TestBed, ComponentFixture, async} from '@angular/core/testing';

import {DIR_DOCUMENT} from '../../bidi/directionality';
import {DEFAULT_BREAKPOINTS_PROVIDER} from '../../media-query/breakpoints/break-points-provider';
import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry';
import {MockMatchMedia} from '../../media-query/mock/mock-match-media';
Expand All @@ -26,17 +27,20 @@ describe('layout-gap directive', () => {
let fixture: ComponentFixture<any>;
let createTestComponent = makeCreateTestComponent(() => TestLayoutGapComponent);
let expectDomForQuery = makeExpectDOMForQuery(() => TestLayoutGapComponent);
let fakeDocument: {body: {dir?: string}, documentElement: {dir?: string}};

beforeEach(() => {
jasmine.addMatchers(customMatchers);
fakeDocument = {body: {}, documentElement: {}};

// Configure testbed to prepare services
TestBed.configureTestingModule({
imports: [CommonModule, FlexLayoutModule],
declarations: [TestLayoutGapComponent],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
{provide: DIR_DOCUMENT, useValue: fakeDocument}
]
});
});
Expand Down Expand Up @@ -287,6 +291,23 @@ describe('layout-gap directive', () => {

});

describe('rtl support', () => {
it('uses margin-left when document body has rtl dir', () => {
fakeDocument.body.dir = 'rtl';
verifyCorrectMargin('row', 'margin-left');
});

it('uses margin-left when documentElement has rtl dir', () => {
fakeDocument.documentElement.dir = 'rtl';
verifyCorrectMargin('row', 'margin-left');
});

it('still uses margin-bottom in column layout when body has rtl dir', () => {
fakeDocument.body.dir = 'rtl';
verifyCorrectMargin('column', 'margin-bottom');
});
});

});


Expand Down
12 changes: 10 additions & 2 deletions src/lib/api/flexbox/layout-gap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {Subscription} from 'rxjs/Subscription';

import {BaseFxDirective} from '../core/base';
import {LayoutDirective} from './layout';
import {Directionality} from '../../bidi/directionality';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {LAYOUT_VALUES} from '../../utils/layout-validator';
Expand All @@ -45,6 +46,7 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
protected _layout = 'row'; // default flex-direction
protected _layoutWatcher: Subscription;
protected _observer: MutationObserver;
private _directionWatcher: Subscription;

/* tslint:disable */
@Input('fxLayoutGap') set gap(val) { this._cacheInput('gap', val); }
Expand All @@ -70,12 +72,15 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
renderer: Renderer2,
@Optional() @Self() container: LayoutDirective,
private _zone: NgZone,
@Inject(PLATFORM_ID) platformId: Object) {
@Inject(PLATFORM_ID) platformId: Object,
private _directionality: Directionality) {
super(monitor, elRef, renderer, platformId);

if (container) { // Subscribe to layout direction changes
this._layoutWatcher = container.layout$.subscribe(this._onLayoutChange.bind(this));
}
this._directionWatcher =
this._directionality.change.subscribe(this._updateWithValue.bind(this));
}

// *********************************************
Expand Down Expand Up @@ -108,6 +113,9 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
if (this._observer) {
this._observer.disconnect();
}
if (this._directionWatcher) {
this._directionWatcher.unsubscribe();
}
}

// *********************************************
Expand Down Expand Up @@ -196,7 +204,7 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
case 'row' :
case 'row-reverse':
default :
key = 'margin-right';
key = this._directionality.value === 'rtl' ? 'margin-left' : 'margin-right';
break;
}
margins[key] = value;
Expand Down
23 changes: 23 additions & 0 deletions src/lib/bidi/bidi-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @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 {NgModule} from '@angular/core';
import {DOCUMENT} from '@angular/common';
import {Dir} from './dir';
import {DIR_DOCUMENT, Directionality} from './directionality';


@NgModule({
exports: [Dir],
declarations: [Dir],
providers: [
{provide: DIR_DOCUMENT, useExisting: DOCUMENT},
Directionality,
]
})
export class BidiModule { }
39 changes: 39 additions & 0 deletions src/lib/bidi/bidi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
The `bidi` package provides a common system for components to get and respond to change in the
application's LTR/RTL layout direction. This directory was copied straight from
https://github.com/angular/material2/blob/master/src/cdk/bidi/

### Directionality

When including the CDK's `BidiModule`, components can inject `Directionality` to get the current
text direction (RTL or LTR);

#### Example
```ts
@Component({ ... })
export class MyWidget implements OnDestroy {

/** Whether the widget is in RTL mode or not. */
private isRtl: boolean;

/** Subscription to the Directionality change EventEmitter. */
private _dirChangeSubscription = Subscription.EMPTY;

constructor(dir: Directionality) {
this.isRtl = dir.value === 'rtl';

_dirChangeSubscription = dir.change.subscribe(() => {
this.flipDirection();
});
}

ngOnDestroy() {
this._dirChangeSubscription.unsubscribe();
}
}
```

### The `Dir` directive
The `BidiModule` also includes a directive that matches any elements with a `dir` attribute. This
directive has the same API as Directionality and provides itself _as_ `Directionality`. By doing
this, any component that injects `Directionality` will get the closest ancestor layout direction
context.
Loading

0 comments on commit 7699957

Please sign in to comment.