feat(overlay): support custom offsets (#1660)

This commit is contained in:
Kara
2016-11-03 16:00:03 -07:00
committed by Jeremy Elbourn
parent 16cbbab586
commit 1738d248fb
4 changed files with 106 additions and 14 deletions
@@ -1,5 +1,6 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {Component, ViewChild} from '@angular/core';
import {By} from '@angular/platform-browser';
import {ConnectedOverlayDirective, OverlayModule} from './overlay-directives';
import {OverlayContainer} from './overlay-container';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
@@ -121,6 +122,36 @@ describe('Overlay directives', () => {
expect(fixture.componentInstance.backdropClicked).toBe(true);
});
it('should set the offsetX', () => {
const trigger = fixture.debugElement.query(By.css('button')).nativeElement;
const startX = trigger.getBoundingClientRect().left;
fixture.componentInstance.offsetX = 5;
fixture.componentInstance.isOpen = true;
fixture.detectChanges();
// expected x value is the starting x + offset x
const expectedX = startX + 5;
const pane = overlayContainerElement.children[0] as HTMLElement;
expect(pane.style.transform).toContain(`translateX(${expectedX}px)`);
});
it('should set the offsetY', () => {
const trigger = fixture.debugElement.query(By.css('button')).nativeElement;
trigger.style.position = 'absolute';
trigger.style.top = '30px';
trigger.style.height = '20px';
fixture.componentInstance.offsetY = 45;
fixture.componentInstance.isOpen = true;
fixture.detectChanges();
// expected y value is the starting y + trigger height + offset y
// 30 + 20 + 45 = 95px
const pane = overlayContainerElement.children[0] as HTMLElement;
expect(pane.style.transform).toContain(`translateY(95px)`);
});
});
});
@@ -131,7 +162,7 @@ describe('Overlay directives', () => {
<button overlay-origin #trigger="overlayOrigin">Toggle menu</button>
<template connected-overlay [origin]="trigger" [open]="isOpen" [width]="width" [height]="height"
[hasBackdrop]="hasBackdrop" backdropClass="md-test-class"
(backdropClick)="backdropClicked=true">
(backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY">
<p>Menu content</p>
</template>`,
})
@@ -139,6 +170,8 @@ class ConnectedOverlayDirectiveTest {
isOpen = false;
width: number | string;
height: number | string;
offsetX: number = 0;
offsetY: number = 0;
hasBackdrop: boolean;
backdropClicked = false;
+18 -8
View File
@@ -67,6 +67,12 @@ export class ConnectedOverlayDirective implements OnDestroy {
@Input() origin: OverlayOrigin;
@Input() positions: ConnectionPositionPair[];
/** The offset in pixels for the overlay connection point on the x-axis */
@Input() offsetX: number = 0;
/** The offset in pixels for the overlay connection point on the y-axis */
@Input() offsetY: number = 0;
/** The width of the overlay panel. */
@Input() width: number | string;
@@ -150,20 +156,24 @@ export class ConnectedOverlayDirective implements OnDestroy {
overlayConfig.backdropClass = this.backdropClass;
}
overlayConfig.positionStrategy = this._getPosition();
overlayConfig.positionStrategy = this._createPositionStrategy();
overlayConfig.direction = this.dir;
return overlayConfig;
}
/** Returns the position of the overlay to be set on the overlay config */
private _getPosition(): ConnectedPositionStrategy {
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})
.setDirection(this.dir);
/** Returns the position strategy of the overlay to be set on the overlay config */
private _createPositionStrategy(): ConnectedPositionStrategy {
const pos = this.positions[0];
const originPoint = {originX: pos.originX, originY: pos.originY};
const overlayPoint = {overlayX: pos.overlayX, overlayY: pos.overlayY};
return this._overlay.position()
.connectedTo(this.origin.elementRef, originPoint, overlayPoint)
.withDirection(this.dir)
.withOffsetX(this.offsetX)
.withOffsetY(this.offsetY);
}
/** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */
@@ -215,7 +215,7 @@ describe('ConnectedPositionStrategy', () => {
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'})
.setDirection('rtl');
.withDirection('rtl');
strategy.apply(overlayElement);
@@ -223,6 +223,37 @@ describe('ConnectedPositionStrategy', () => {
expect(overlayRect.top).toBe(originRect.bottom);
expect(overlayRect.right).toBe(originRect.right);
});
it('should position a panel with the x offset provided', () => {
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'});
strategy.withOffsetX(10);
strategy.apply(overlayElement);
let overlayRect = overlayElement.getBoundingClientRect();
expect(overlayRect.top).toBe(originRect.top);
expect(overlayRect.left).toBe(originRect.left + 10);
});
it('should position a panel with the y offset provided', () => {
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'});
strategy.withOffsetY(50);
strategy.apply(overlayElement);
let overlayRect = overlayElement.getBoundingClientRect();
expect(overlayRect.top).toBe(originRect.top + 50);
expect(overlayRect.left).toBe(originRect.left);
});
});
@@ -11,7 +11,7 @@ import {
/**
* A strategy for positioning overlays. Using this strategy, an overlay is given an
* implict position relative some origin element. The relative position is defined in terms of
* implicit position relative some origin element. The relative position is defined in terms of
* a point on the origin element that is connected to a point on the overlay element. For example,
* a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner
* of the overlay.
@@ -19,6 +19,12 @@ import {
export class ConnectedPositionStrategy implements PositionStrategy {
private _dir = 'ltr';
/** The offset in pixels for the overlay connection point on the x-axis */
private _offsetX: number = 0;
/** The offset in pixels for the overlay connection point on the y-axis */
private _offsetY: number = 0;
/** Whether the we're dealing with an RTL context */
get _isRtl() {
return this._dir === 'rtl';
@@ -89,11 +95,23 @@ export class ConnectedPositionStrategy implements PositionStrategy {
}
/** Sets the layout direction so the overlay's position can be adjusted to match. */
setDirection(dir: 'ltr' | 'rtl') {
withDirection(dir: 'ltr' | 'rtl'): this {
this._dir = dir;
return this;
}
/** Sets an offset for the overlay's connection point on the x-axis */
withOffsetX(offset: number): this {
this._offsetX = offset;
return this;
}
/** Sets an offset for the overlay's connection point on the y-axis */
withOffsetY(offset: number): this {
this._offsetY = offset;
return this;
}
/**
* Gets the horizontal (x) "start" dimension based on whether the overlay is in an RTL context.
* @param rect
@@ -168,8 +186,8 @@ export class ConnectedPositionStrategy implements PositionStrategy {
}
return {
x: originPoint.x + overlayStartX,
y: originPoint.y + overlayStartY
x: originPoint.x + overlayStartX + this._offsetX,
y: originPoint.y + overlayStartY + this._offsetY
};
}