mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(tabs): re-align ink bar on direction change (#3622)
Re-aligns the ink bar if the user's layout direction changed. Fixes #3615.
This commit is contained in:
committed by
tinayuangao
parent
52ea7a34e2
commit
07793a48ea
+14
-6
@@ -1,4 +1,4 @@
|
||||
import {Directive, Renderer, ElementRef} from '@angular/core';
|
||||
import {Directive, Renderer, ElementRef, NgZone} from '@angular/core';
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,10 @@ import {Directive, Renderer, ElementRef} from '@angular/core';
|
||||
},
|
||||
})
|
||||
export class MdInkBar {
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
constructor(
|
||||
private _renderer: Renderer,
|
||||
private _elementRef: ElementRef,
|
||||
private _ngZone: NgZone) {}
|
||||
|
||||
/**
|
||||
* Calculates the styles from the provided element in order to align the ink-bar to that element.
|
||||
@@ -21,10 +24,15 @@ export class MdInkBar {
|
||||
*/
|
||||
alignToElement(element: HTMLElement) {
|
||||
this.show();
|
||||
this._renderer.setElementStyle(this._elementRef.nativeElement, 'left',
|
||||
this._getLeftPosition(element));
|
||||
this._renderer.setElementStyle(this._elementRef.nativeElement, 'width',
|
||||
this._getElementWidth(element));
|
||||
|
||||
this._ngZone.runOutsideAngular(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this._renderer.setElementStyle(this._elementRef.nativeElement, 'left',
|
||||
this._getLeftPosition(element));
|
||||
this._renderer.setElementStyle(this._elementRef.nativeElement, 'width',
|
||||
this._getElementWidth(element));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Shows the ink bar. */
|
||||
|
||||
@@ -11,10 +11,12 @@ import {RIGHT_ARROW, LEFT_ARROW, ENTER} from '../core/keyboard/keycodes';
|
||||
import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler';
|
||||
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
|
||||
import {dispatchKeyboardEvent} from '../core/testing/dispatch-events';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
|
||||
|
||||
describe('MdTabHeader', () => {
|
||||
let dir: LayoutDirection = 'ltr';
|
||||
let dirChange = new Subject();
|
||||
let fixture: ComponentFixture<SimpleTabHeaderApp>;
|
||||
let appComponent: SimpleTabHeaderApp;
|
||||
|
||||
@@ -29,7 +31,9 @@ describe('MdTabHeader', () => {
|
||||
SimpleTabHeaderApp,
|
||||
],
|
||||
providers: [
|
||||
{provide: Dir, useFactory: () => { return {value: dir}; }},
|
||||
{provide: Dir, useFactory: () => {
|
||||
return {value: dir, dirChange: dirChange.asObservable()};
|
||||
}},
|
||||
{provide: ViewportRuler, useClass: FakeViewportRuler},
|
||||
]
|
||||
});
|
||||
@@ -192,8 +196,22 @@ describe('MdTabHeader', () => {
|
||||
expect(appComponent.mdTabHeader.scrollDistance).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
expect(inkBar.alignToElement).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
interface Tab {
|
||||
@@ -211,7 +229,7 @@ interface Tab {
|
||||
*ngFor="let tab of tabs; let i = index"
|
||||
[disabled]="!!tab.disabled"
|
||||
(click)="selectedIndex = i">
|
||||
{{tab.label}}
|
||||
{{tab.label}}
|
||||
</div>
|
||||
</md-tab-header>
|
||||
</div>
|
||||
|
||||
+20
-11
@@ -2,7 +2,6 @@ import {
|
||||
ViewChild,
|
||||
Component,
|
||||
Input,
|
||||
NgZone,
|
||||
QueryList,
|
||||
ElementRef,
|
||||
ViewEncapsulation,
|
||||
@@ -12,12 +11,14 @@ import {
|
||||
Optional,
|
||||
AfterContentChecked,
|
||||
AfterContentInit,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import {RIGHT_ARROW, LEFT_ARROW, ENTER, Dir, LayoutDirection} from '../core';
|
||||
import {MdTabLabelWrapper} from './tab-label-wrapper';
|
||||
import {MdInkBar} from './ink-bar';
|
||||
import 'rxjs/add/operator/map';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {applyCssTransform} from '../core/style/apply-transform';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
/**
|
||||
* The directions that scrolling can go in when the header's tabs exceed the header width. 'After'
|
||||
@@ -51,7 +52,7 @@ const EXAGGERATED_OVERSCROLL = 60;
|
||||
'[class.mat-tab-header-rtl]': "_getLayoutDirection() == 'rtl'",
|
||||
}
|
||||
})
|
||||
export class MdTabHeader implements AfterContentChecked, AfterContentInit {
|
||||
export class MdTabHeader implements AfterContentChecked, AfterContentInit, OnDestroy {
|
||||
@ContentChildren(MdTabLabelWrapper) _labelWrappers: QueryList<MdTabLabelWrapper>;
|
||||
|
||||
@ViewChild(MdInkBar) _inkBar: MdInkBar;
|
||||
@@ -67,6 +68,9 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit {
|
||||
/** Whether the header should scroll to the selected index after the view has been checked. */
|
||||
private _selectedIndexChanged = false;
|
||||
|
||||
/** Subscription to changes in the layout direction. */
|
||||
private _directionChange: Subscription;
|
||||
|
||||
/** Whether the controls for pagination should be displayed */
|
||||
_showPaginationControls = false;
|
||||
|
||||
@@ -102,9 +106,7 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit {
|
||||
/** Event emitted when a label is focused. */
|
||||
@Output() indexFocused = new EventEmitter();
|
||||
|
||||
constructor(private _zone: NgZone,
|
||||
private _elementRef: ElementRef,
|
||||
@Optional() private _dir: Dir) {}
|
||||
constructor(private _elementRef: ElementRef, @Optional() private _dir: Dir) {}
|
||||
|
||||
ngAfterContentChecked(): void {
|
||||
// If the number of tab labels have changed, check if scrolling should be enabled
|
||||
@@ -149,6 +151,17 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit {
|
||||
*/
|
||||
ngAfterContentInit() {
|
||||
this._alignInkBarToSelectedTab();
|
||||
|
||||
if (this._dir) {
|
||||
this._directionChange = this._dir.dirChange.subscribe(() => this._alignInkBarToSelectedTab());
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this._directionChange) {
|
||||
this._directionChange.unsubscribe();
|
||||
this._directionChange = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,10 +386,6 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit {
|
||||
? this._labelWrappers.toArray()[this.selectedIndex].elementRef.nativeElement
|
||||
: null;
|
||||
|
||||
this._zone.runOutsideAngular(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this._inkBar.alignToElement(selectedLabelWrapper);
|
||||
});
|
||||
});
|
||||
this._inkBar.alignToElement(selectedLabelWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@ import {
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
ViewEncapsulation,
|
||||
Directive, NgZone, Inject, Optional,
|
||||
Directive,
|
||||
NgZone,
|
||||
Inject,
|
||||
Optional,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import {MdInkBar} from '../ink-bar';
|
||||
import {MdRipple} from '../../core/ripple/index';
|
||||
import {ViewportRuler} from '../../core/overlay/position/viewport-ruler';
|
||||
import {MD_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions} from '../../core/ripple/ripple';
|
||||
import {MD_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions, Dir} from '../../core';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
|
||||
/**
|
||||
* Navigation component matching the styles of the tab group header.
|
||||
@@ -25,12 +30,19 @@ import {MD_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions} from '../../core/ripple/r
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class MdTabNavBar {
|
||||
export class MdTabNavBar implements OnDestroy {
|
||||
private _directionChange: Subscription;
|
||||
_activeLinkChanged: boolean;
|
||||
_activeLinkElement: ElementRef;
|
||||
|
||||
@ViewChild(MdInkBar) _inkBar: MdInkBar;
|
||||
|
||||
constructor(@Optional() private _dir: Dir) {
|
||||
if (_dir) {
|
||||
this._directionChange = _dir.dirChange.subscribe(() => this._alignInkBar());
|
||||
}
|
||||
}
|
||||
|
||||
/** Notifies the component that the active link has been changed. */
|
||||
updateActiveLink(element: ElementRef) {
|
||||
this._activeLinkChanged = this._activeLinkElement != element;
|
||||
@@ -40,10 +52,22 @@ export class MdTabNavBar {
|
||||
/** Checks if the active link has been changed and, if so, will update the ink bar. */
|
||||
ngAfterContentChecked(): void {
|
||||
if (this._activeLinkChanged) {
|
||||
this._inkBar.alignToElement(this._activeLinkElement.nativeElement);
|
||||
this._alignInkBar();
|
||||
this._activeLinkChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this._directionChange) {
|
||||
this._directionChange.unsubscribe();
|
||||
this._directionChange = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Aligns the ink bar to the active link. */
|
||||
private _alignInkBar(): void {
|
||||
this._inkBar.alignToElement(this._activeLinkElement.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user