Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cdk/overlay): add start and end positions to GlobalPositionStrategy #12007

Merged
merged 1 commit into from
Feb 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/cdk/overlay/position/global-position-strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ describe('GlobalPositonStrategy', () => {
expect(parentStyle.alignItems).toBe('flex-end');
});

it('should not set any alignment by default', () => {
attachOverlay({
positionStrategy: overlay.position().global(),
});

const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(parentStyle.justifyContent).toBe('');
expect(parentStyle.alignItems).toBe('');
});

it('should center the element', () => {
attachOverlay({
positionStrategy: overlay.position().global().centerHorizontally().centerVertically(),
Expand All @@ -121,13 +132,36 @@ describe('GlobalPositonStrategy', () => {
const elementStyle = overlayRef.overlayElement.style;
const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(elementStyle.marginRight).toBe('');
expect(elementStyle.marginBottom).toBe('');
expect(elementStyle.marginLeft).toBe('10px');
expect(elementStyle.marginTop).toBe('15px');

expect(parentStyle.justifyContent).toBe('center');
expect(parentStyle.alignItems).toBe('center');
});

it('should center the element with an offset in rtl', () => {
attachOverlay({
direction: 'rtl',
positionStrategy: overlay
.position()
.global()
.centerHorizontally('10px')
.centerVertically('15px'),
});

const elementStyle = overlayRef.overlayElement.style;
const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(elementStyle.marginLeft).toBe('');
expect(elementStyle.marginRight).toBe('10px');
expect(elementStyle.marginTop).toBe('15px');

expect(parentStyle.justifyContent).toBe('center');
expect(parentStyle.alignItems).toBe('center');
});

it('should make the element position: static', () => {
attachOverlay({
positionStrategy: overlay.position().global(),
Expand Down Expand Up @@ -367,6 +401,62 @@ describe('GlobalPositonStrategy', () => {
expect(parentStyle.justifyContent).toBeFalsy();
expect(parentStyle.alignItems).toBeFalsy();
});

it('should position the overlay to the start in ltr', () => {
attachOverlay({
direction: 'ltr',
positionStrategy: overlay.position().global().start('40px'),
});

const elementStyle = overlayRef.overlayElement.style;
const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(elementStyle.marginLeft).toBe('40px');
expect(elementStyle.marginRight).toBe('');
expect(parentStyle.justifyContent).toBe('flex-start');
});

it('should position the overlay to the start in rtl', () => {
attachOverlay({
direction: 'rtl',
positionStrategy: overlay.position().global().start('50px'),
});

const elementStyle = overlayRef.overlayElement.style;
const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(elementStyle.marginLeft).toBe('');
expect(elementStyle.marginRight).toBe('50px');
expect(parentStyle.justifyContent).toBe('flex-start');
});

it('should position the overlay to the end in ltr', () => {
attachOverlay({
direction: 'ltr',
positionStrategy: overlay.position().global().end('60px'),
});

const elementStyle = overlayRef.overlayElement.style;
const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(elementStyle.marginRight).toBe('60px');
expect(elementStyle.marginLeft).toBe('');
expect(parentStyle.justifyContent).toBe('flex-end');
});

it('should position the overlay to the end in rtl', () => {
attachOverlay({
direction: 'rtl',
positionStrategy: overlay.position().global().end('70px'),
});

const elementStyle = overlayRef.overlayElement.style;
const parentStyle = (overlayRef.overlayElement.parentNode as HTMLElement).style;

expect(elementStyle.marginLeft).toBe('70px');
expect(elementStyle.marginRight).toBe('');
expect(parentStyle.justifyContent).toBe('flex-end');
});
});

@Component({template: ''})
Expand Down
107 changes: 70 additions & 37 deletions src/cdk/overlay/position/global-position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@ const wrapperClass = 'cdk-global-overlay-wrapper';
export class GlobalPositionStrategy implements PositionStrategy {
/** The overlay to which this strategy is attached. */
private _overlayRef: OverlayReference;
private _cssPosition: string = 'static';
private _topOffset: string = '';
private _bottomOffset: string = '';
private _leftOffset: string = '';
private _rightOffset: string = '';
private _alignItems: string = '';
private _justifyContent: string = '';
private _width: string = '';
private _height: string = '';
private _isDisposed: boolean;
private _cssPosition = 'static';
private _topOffset = '';
private _bottomOffset = '';
private _alignItems = '';
private _xPosition = '';
private _xOffset = '';
private _width = '';
private _height = '';
private _isDisposed = false;

attach(overlayRef: OverlayReference): void {
const config = overlayRef.getConfig();
Expand Down Expand Up @@ -65,9 +64,8 @@ export class GlobalPositionStrategy implements PositionStrategy {
* @param value New left offset.
*/
left(value: string = ''): this {
this._rightOffset = '';
this._leftOffset = value;
this._justifyContent = 'flex-start';
this._xOffset = value;
this._xPosition = 'left';
return this;
}

Expand All @@ -87,9 +85,30 @@ export class GlobalPositionStrategy implements PositionStrategy {
* @param value New right offset.
*/
right(value: string = ''): this {
this._leftOffset = '';
this._rightOffset = value;
this._justifyContent = 'flex-end';
this._xOffset = value;
this._xPosition = 'right';
return this;
}

/**
* Sets the overlay to the start of the viewport, depending on the overlay direction.
* This will be to the left in LTR layouts and to the right in RTL.
* @param offset Offset from the edge of the screen.
*/
start(value: string = ''): this {
this._xOffset = value;
this._xPosition = 'start';
return this;
}

/**
* Sets the overlay to the end of the viewport, depending on the overlay direction.
* This will be to the right in LTR layouts and to the left in RTL.
* @param offset Offset from the edge of the screen.
*/
end(value: string = ''): this {
this._xOffset = value;
this._xPosition = 'end';
return this;
}

Expand Down Expand Up @@ -133,7 +152,7 @@ export class GlobalPositionStrategy implements PositionStrategy {
*/
centerHorizontally(offset: string = ''): this {
this.left(offset);
this._justifyContent = 'center';
this._xPosition = 'center';
return this;
}

Expand Down Expand Up @@ -171,31 +190,45 @@ export class GlobalPositionStrategy implements PositionStrategy {
const shouldBeFlushVertically =
(height === '100%' || height === '100vh') &&
(!maxHeight || maxHeight === '100%' || maxHeight === '100vh');

styles.position = this._cssPosition;
styles.marginLeft = shouldBeFlushHorizontally ? '0' : this._leftOffset;
styles.marginTop = shouldBeFlushVertically ? '0' : this._topOffset;
styles.marginBottom = this._bottomOffset;
styles.marginRight = this._rightOffset;
const xPosition = this._xPosition;
const xOffset = this._xOffset;
const isRtl = this._overlayRef.getConfig().direction === 'rtl';
let marginLeft = '';
let marginRight = '';
let justifyContent = '';

if (shouldBeFlushHorizontally) {
parentStyles.justifyContent = 'flex-start';
} else if (this._justifyContent === 'center') {
parentStyles.justifyContent = 'center';
} else if (this._overlayRef.getConfig().direction === 'rtl') {
// In RTL the browser will invert `flex-start` and `flex-end` automatically, but we
// don't want that because our positioning is explicitly `left` and `right`, hence
// why we do another inversion to ensure that the overlay stays in the same position.
// TODO: reconsider this if we add `start` and `end` methods.
if (this._justifyContent === 'flex-start') {
parentStyles.justifyContent = 'flex-end';
} else if (this._justifyContent === 'flex-end') {
parentStyles.justifyContent = 'flex-start';
justifyContent = 'flex-start';
} else if (xPosition === 'center') {
justifyContent = 'center';

if (isRtl) {
marginRight = xOffset;
} else {
marginLeft = xOffset;
}
} else {
parentStyles.justifyContent = this._justifyContent;
} else if (isRtl) {
if (xPosition === 'left' || xPosition === 'end') {
justifyContent = 'flex-end';
marginLeft = xOffset;
} else if (xPosition === 'right' || xPosition === 'start') {
justifyContent = 'flex-start';
marginRight = xOffset;
}
} else if (xPosition === 'left' || xPosition === 'start') {
justifyContent = 'flex-start';
marginLeft = xOffset;
} else if (xPosition === 'right' || xPosition === 'end') {
justifyContent = 'flex-end';
marginRight = xOffset;
}

styles.position = this._cssPosition;
styles.marginLeft = shouldBeFlushHorizontally ? '0' : marginLeft;
styles.marginTop = shouldBeFlushVertically ? '0' : this._topOffset;
styles.marginBottom = this._bottomOffset;
styles.marginRight = shouldBeFlushHorizontally ? '0' : marginRight;
parentStyles.justifyContent = justifyContent;
parentStyles.alignItems = shouldBeFlushVertically ? 'flex-start' : this._alignItems;
}

Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/cdk/overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ export class GlobalPositionStrategy implements PositionStrategy {
centerHorizontally(offset?: string): this;
centerVertically(offset?: string): this;
dispose(): void;
end(value?: string): this;
// @deprecated
height(value?: string): this;
left(value?: string): this;
right(value?: string): this;
start(value?: string): this;
top(value?: string): this;
// @deprecated
width(value?: string): this;
Expand Down