mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(tooltip) add RTL support (#1830)
* feat(tooltip): support rtl * remove fdescribe * more changes for before, after * fix test * rebase * minor change
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
<md-radio-group [(ngModel)]="position">
|
||||
<md-radio-button value="below">Below</md-radio-button>
|
||||
<md-radio-button value="above">Above</md-radio-button>
|
||||
<md-radio-button value="left">Left</md-radio-button>
|
||||
<md-radio-button value="right">Right</md-radio-button>
|
||||
<md-radio-button value="before">Before</md-radio-button>
|
||||
<md-radio-button value="after">After</md-radio-button>
|
||||
</md-radio-group>
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
# MdTooltip
|
||||
Tooltip allows the user to specify text to be displayed when the mouse hover over an element.
|
||||
Tooltip allows the user to specify text to be displayed when the mouse hovers over an element.
|
||||
|
||||
The positions `before` and `after` should be used instead of `left` and `right` respectively when the tooltip positioning should change depending on the HTML dir global attribute for left-to-right or right-to-left layout.
|
||||
|
||||
### Examples
|
||||
A button with a tooltip
|
||||
```html
|
||||
<button md-tooltip="some message" tooltip-position="below">Button</button>
|
||||
<button md-tooltip="some message" tooltip-position="bottom">Button</button>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## `[md-tooltip]`
|
||||
### Properties
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `md-tooltip` | `string` | The message to be displayed. |
|
||||
| `tooltip-position` | `"above"|"below"|"before"|"after"` | The position of the tooltip. |
|
||||
| `tooltip-position` | `"before"|"after"|"above"|"below"|"left"|"right"` | The position of the tooltip. |
|
||||
|
||||
### Methods
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- | --- |
|
||||
| --- | --- |
|
||||
| `show` | Displays the tooltip. |
|
||||
| `hide` | Removes the tooltip. |
|
||||
| `toggle` | Displays or hides the tooltip. |
|
||||
|
||||
@@ -10,12 +10,13 @@ import {Component, DebugElement, AnimationTransitionEvent} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {TooltipPosition, MdTooltip, MdTooltipModule} from './tooltip';
|
||||
import {OverlayContainer} from '../core';
|
||||
import {Dir, LayoutDirection} from '../core/rtl/dir';
|
||||
|
||||
const initialTooltipMessage = 'initial tooltip message';
|
||||
|
||||
describe('MdTooltip', () => {
|
||||
let overlayContainerElement: HTMLElement;
|
||||
|
||||
let dir: {value: LayoutDirection};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -25,6 +26,9 @@ describe('MdTooltip', () => {
|
||||
{provide: OverlayContainer, useFactory: () => {
|
||||
overlayContainerElement = document.createElement('div');
|
||||
return {getContainerElement: () => overlayContainerElement};
|
||||
}},
|
||||
{provide: Dir, useFactory: () => {
|
||||
return dir = { value: 'ltr' };
|
||||
}}
|
||||
]
|
||||
});
|
||||
@@ -155,6 +159,76 @@ describe('MdTooltip', () => {
|
||||
phaseName: '',
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should consistently position before and after overlay origin in ltr and rtl dir', () => {
|
||||
tooltipDirective.position = 'left';
|
||||
const leftOrigin = tooltipDirective._getOrigin();
|
||||
tooltipDirective.position = 'right';
|
||||
const rightOrigin = tooltipDirective._getOrigin();
|
||||
|
||||
// Test expectations in LTR
|
||||
tooltipDirective.position = 'before';
|
||||
expect(tooltipDirective._getOrigin()).toEqual(leftOrigin);
|
||||
tooltipDirective.position = 'after';
|
||||
expect(tooltipDirective._getOrigin()).toEqual(rightOrigin);
|
||||
|
||||
// Test expectations in LTR
|
||||
dir.value = 'rtl';
|
||||
tooltipDirective.position = 'before';
|
||||
expect(tooltipDirective._getOrigin()).toEqual(rightOrigin);
|
||||
tooltipDirective.position = 'after';
|
||||
expect(tooltipDirective._getOrigin()).toEqual(leftOrigin);
|
||||
});
|
||||
|
||||
it('should consistently position before and after overlay position in ltr and rtl dir', () => {
|
||||
tooltipDirective.position = 'left';
|
||||
const leftOverlayPosition = tooltipDirective._getOverlayPosition();
|
||||
tooltipDirective.position = 'right';
|
||||
const rightOverlayPosition = tooltipDirective._getOverlayPosition();
|
||||
|
||||
// Test expectations in LTR
|
||||
tooltipDirective.position = 'before';
|
||||
expect(tooltipDirective._getOverlayPosition()).toEqual(leftOverlayPosition);
|
||||
tooltipDirective.position = 'after';
|
||||
expect(tooltipDirective._getOverlayPosition()).toEqual(rightOverlayPosition);
|
||||
|
||||
// Test expectations in LTR
|
||||
dir.value = 'rtl';
|
||||
tooltipDirective.position = 'before';
|
||||
expect(tooltipDirective._getOverlayPosition()).toEqual(rightOverlayPosition);
|
||||
tooltipDirective.position = 'after';
|
||||
expect(tooltipDirective._getOverlayPosition()).toEqual(leftOverlayPosition);
|
||||
});
|
||||
|
||||
it('should have consistent left transform origin in any dir', () => {
|
||||
tooltipDirective.position = 'right';
|
||||
tooltipDirective.show();
|
||||
expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('left');
|
||||
|
||||
tooltipDirective.position = 'after';
|
||||
tooltipDirective.show();
|
||||
expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('left');
|
||||
|
||||
dir.value = 'rtl';
|
||||
tooltipDirective.position = 'before';
|
||||
tooltipDirective.show();
|
||||
expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('left');
|
||||
});
|
||||
|
||||
it('should have consistent right transform origin in any dir', () => {
|
||||
tooltipDirective.position = 'left';
|
||||
tooltipDirective.show();
|
||||
expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('right');
|
||||
|
||||
tooltipDirective.position = 'before';
|
||||
tooltipDirective.show();
|
||||
expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('right');
|
||||
|
||||
dir.value = 'rtl';
|
||||
tooltipDirective.position = 'after';
|
||||
tooltipDirective.show();
|
||||
expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('right');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+50
-19
@@ -13,6 +13,7 @@ import {
|
||||
animate,
|
||||
AnimationTransitionEvent,
|
||||
NgZone,
|
||||
Optional,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
Overlay,
|
||||
@@ -27,9 +28,9 @@ import {
|
||||
} from '../core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {Dir} from '../core/rtl/dir';
|
||||
|
||||
|
||||
export type TooltipPosition = 'before' | 'after' | 'above' | 'below';
|
||||
export type TooltipPosition = 'left' | 'right' | 'above' | 'below' | 'before' | 'after';
|
||||
|
||||
/** Time in ms to delay before changing the tooltip visibility to hidden */
|
||||
export const TOUCHEND_HIDE_DELAY = 1500;
|
||||
@@ -85,7 +86,8 @@ export class MdTooltip {
|
||||
}
|
||||
|
||||
constructor(private _overlay: Overlay, private _elementRef: ElementRef,
|
||||
private _viewContainerRef: ViewContainerRef, private _ngZone: NgZone) {}
|
||||
private _viewContainerRef: ViewContainerRef, private _ngZone: NgZone,
|
||||
@Optional() private _dir: Dir) {}
|
||||
|
||||
/** Dispose the tooltip when destroyed */
|
||||
ngOnDestroy() {
|
||||
@@ -155,22 +157,46 @@ export class MdTooltip {
|
||||
}
|
||||
|
||||
/** Returns the origin position based on the user's position preference */
|
||||
private _getOrigin(): OriginConnectionPosition {
|
||||
switch (this.position) {
|
||||
case 'before': return { originX: 'start', originY: 'center' };
|
||||
case 'after': return { originX: 'end', originY: 'center' };
|
||||
case 'above': return { originX: 'center', originY: 'top' };
|
||||
case 'below': return { originX: 'center', originY: 'bottom' };
|
||||
_getOrigin(): OriginConnectionPosition {
|
||||
if (this.position == 'above' || this.position == 'below') {
|
||||
return {originX: 'center', originY: this.position == 'above' ? 'top' : 'bottom'};
|
||||
}
|
||||
|
||||
const isDirectionLtr = !this._dir || this._dir.value == 'ltr';
|
||||
if (this.position == 'left' ||
|
||||
this.position == 'before' && isDirectionLtr ||
|
||||
this.position == 'after' && !isDirectionLtr) {
|
||||
return {originX: 'start', originY: 'center'};
|
||||
}
|
||||
|
||||
if (this.position == 'right' ||
|
||||
this.position == 'after' && isDirectionLtr ||
|
||||
this.position == 'before' && !isDirectionLtr) {
|
||||
return {originX: 'end', originY: 'center'};
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the overlay position based on the user's preference */
|
||||
private _getOverlayPosition(): OverlayConnectionPosition {
|
||||
switch (this.position) {
|
||||
case 'before': return { overlayX: 'end', overlayY: 'center' };
|
||||
case 'after': return { overlayX: 'start', overlayY: 'center' };
|
||||
case 'above': return { overlayX: 'center', overlayY: 'bottom' };
|
||||
case 'below': return { overlayX: 'center', overlayY: 'top' };
|
||||
_getOverlayPosition(): OverlayConnectionPosition {
|
||||
if (this.position == 'above') {
|
||||
return {overlayX: 'center', overlayY: 'bottom'};
|
||||
}
|
||||
|
||||
if (this.position == 'below') {
|
||||
return {overlayX: 'center', overlayY: 'top'};
|
||||
}
|
||||
|
||||
const isLtr = !this._dir || this._dir.value == 'ltr';
|
||||
if (this.position == 'left' ||
|
||||
this.position == 'before' && isLtr ||
|
||||
this.position == 'after' && !isLtr) {
|
||||
return {overlayX: 'end', overlayY: 'center'};
|
||||
}
|
||||
|
||||
if (this.position == 'right' ||
|
||||
this.position == 'after' && isLtr ||
|
||||
this.position == 'before' && !isLtr) {
|
||||
return {overlayX: 'start', overlayY: 'center'};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +252,8 @@ export class TooltipComponent {
|
||||
/** Subject for notifying that the tooltip has been hidden from the view */
|
||||
private _onHide: Subject<any> = new Subject();
|
||||
|
||||
constructor(@Optional() private _dir: Dir) {}
|
||||
|
||||
/** Shows the tooltip with an animation originating from the provided origin */
|
||||
show(position: TooltipPosition): void {
|
||||
this._closeOnInteraction = false;
|
||||
@@ -262,11 +290,14 @@ export class TooltipComponent {
|
||||
|
||||
/** Sets the tooltip transform origin according to the tooltip position */
|
||||
_setTransformOrigin(value: TooltipPosition) {
|
||||
const isLtr = !this._dir || this._dir.value == 'ltr';
|
||||
switch (value) {
|
||||
case 'before': this._transformOrigin = 'right'; break;
|
||||
case 'after': this._transformOrigin = 'left'; break;
|
||||
case 'above': this._transformOrigin = 'bottom'; break;
|
||||
case 'below': this._transformOrigin = 'top'; break;
|
||||
case 'before': this._transformOrigin = isLtr ? 'right' : 'left'; break;
|
||||
case 'after': this._transformOrigin = isLtr ? 'left' : 'right'; break;
|
||||
case 'left': this._transformOrigin = 'right'; break;
|
||||
case 'right': this._transformOrigin = 'left'; break;
|
||||
case 'above': this._transformOrigin = 'bottom'; break;
|
||||
case 'below': this._transformOrigin = 'top'; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user