Skip to content

Commit

Permalink
feat(overlay): support rtl in overlays (#1593)
Browse files Browse the repository at this point in the history
  • Loading branch information
kara committed Oct 25, 2016
1 parent c0e6f83 commit b56f520
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 5 deletions.
14 changes: 12 additions & 2 deletions src/lib/core/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EventEmitter,
TemplateRef,
ViewContainerRef,
Optional,
Input,
OnDestroy,
Output,
Expand All @@ -18,6 +19,7 @@ import {ConnectionPositionPair} from './position/connected-position';
import {PortalModule} from '../portal/portal-directives';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {Subscription} from 'rxjs/Subscription';
import {Dir, LayoutDirection} from '../rtl/dir';

/** Default set of positions for the overlay. Follows the behavior of a dropdown. */
let defaultPositionList = [
Expand Down Expand Up @@ -103,14 +105,19 @@ export class ConnectedOverlayDirective implements OnDestroy {
constructor(
private _overlay: Overlay,
templateRef: TemplateRef<any>,
viewContainerRef: ViewContainerRef) {
viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir) {
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
}

get overlayRef(): OverlayRef {
return this._overlayRef;
}

get dir(): LayoutDirection {
return this._dir ? this._dir.value : 'ltr';
}

/** TODO: internal */
ngOnDestroy() {
this._destroyOverlay();
Expand Down Expand Up @@ -145,6 +152,8 @@ export class ConnectedOverlayDirective implements OnDestroy {

overlayConfig.positionStrategy = this._getPosition();

overlayConfig.direction = this.dir;

return overlayConfig;
}

Expand All @@ -153,7 +162,8 @@ export class ConnectedOverlayDirective implements OnDestroy {
return this._overlay.position().connectedTo(
this.origin.elementRef,
{originX: this.positions[0].overlayX, originY: this.positions[0].originY},
{overlayX: this.positions[0].overlayX, overlayY: this.positions[0].overlayY});
{overlayX: this.positions[0].overlayX, overlayY: this.positions[0].overlayY})
.setDirection(this.dir);
}

/** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */
Expand Down
6 changes: 6 additions & 0 deletions src/lib/core/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class OverlayRef implements PortalHost {

let attachResult = this._portalHost.attach(portal);
this.updateSize();
this.updateDirection();
this.updatePosition();

return attachResult;
Expand Down Expand Up @@ -59,6 +60,11 @@ export class OverlayRef implements PortalHost {
}
}

/** Updates the text direction of the overlay panel. **/
private updateDirection() {
this._pane.setAttribute('dir', this._state.direction);
}

/** Updates the size of the overlay based on the overlay config. */
updateSize() {
if (this._state.width || this._state.width === 0) {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/core/overlay/overlay-state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {PositionStrategy} from './position/position-strategy';
import {LayoutDirection} from '../rtl/dir';


/**
Expand All @@ -21,6 +22,9 @@ export class OverlayState {
/** The height of the overlay panel. If a number is provided, pixel units are assumed. **/
height: number | string;

/** The direction of the text in the overlay panel. */
direction: LayoutDirection = 'ltr';

// TODO(jelbourn): configuration still to add
// - focus trap
// - disable pointer events
Expand Down
10 changes: 10 additions & 0 deletions src/lib/core/overlay/overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ describe('Overlay', () => {
expect(overlayContainerElement.textContent).toBe('');
});

it('should set the direction', () => {
const state = new OverlayState();
state.direction = 'rtl';

overlay.create(state).attach(componentPortal);

const pane = overlayContainerElement.children[0] as HTMLElement;
expect(pane.getAttribute('dir')).toEqual('rtl');
});

describe('positioning', () => {
let state: OverlayState;

Expand Down
17 changes: 17 additions & 0 deletions src/lib/core/overlay/position/connected-position-strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,23 @@ describe('ConnectedPositionStrategy', () => {
expect(overlayRect.top).toBe(originRect.bottom);
expect(overlayRect.right).toBe(originRect.left);
});

it('should position a panel properly when rtl', () => {
// must make the overlay longer than the origin to properly test attachment
overlayElement.style.width = `500px`;
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'})
.setDirection('rtl');

strategy.apply(overlayElement);

let overlayRect = overlayElement.getBoundingClientRect();
expect(overlayRect.top).toBe(originRect.bottom);
expect(overlayRect.right).toBe(originRect.right);
});
});


Expand Down
16 changes: 13 additions & 3 deletions src/lib/core/overlay/position/connected-position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ import {
* of the overlay.
*/
export class ConnectedPositionStrategy implements PositionStrategy {
// TODO(jelbourn): set RTL to the actual value from the app.
private _dir = 'ltr';

/** Whether the we're dealing with an RTL context */
_isRtl: boolean = false;
get _isRtl() {
return this._dir === 'rtl';
}

/** Ordered list of preferred positions, from most to least desirable. */
_preferredPositions: ConnectionPositionPair[] = [];
Expand Down Expand Up @@ -85,6 +88,11 @@ export class ConnectedPositionStrategy implements PositionStrategy {
return this;
}

/** Sets the layout direction so the overlay's position can be adjusted to match. */
setDirection(dir: 'ltr' | 'rtl') {
this._dir = dir;
return this;
}

/**
* Gets the horizontal (x) "start" dimension based on whether the overlay is in an RTL context.
Expand Down Expand Up @@ -146,8 +154,10 @@ export class ConnectedPositionStrategy implements PositionStrategy {
let overlayStartX: number;
if (pos.overlayX == 'center') {
overlayStartX = -overlayRect.width / 2;
} else if (pos.overlayX === 'start') {
overlayStartX = this._isRtl ? -overlayRect.width : 0;
} else {
overlayStartX = pos.overlayX == 'start' ? 0 : -overlayRect.width;
overlayStartX = this._isRtl ? 0 : -overlayRect.width;
}

let overlayStartY: number;
Expand Down

0 comments on commit b56f520

Please sign in to comment.