From aebbe66c1bfc420b82f08905de52ef9c423e0c8b Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 5 Oct 2017 21:54:09 +0200 Subject: [PATCH] fix(menu): wrong offset for nested menu in a fallback position * Adds the ability to set offsets on connected position fallbacks. * Fixes wrong positioning of nested menu if they're in a fallback position. Fixes #7549. --- .../connected-position-strategy.spec.ts | 22 +++++++++++++++++++ .../position/connected-position-strategy.ts | 16 ++++++++++---- .../overlay/position/connected-position.ts | 7 +++++- src/lib/menu/menu-trigger.ts | 9 ++++---- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/cdk/overlay/position/connected-position-strategy.spec.ts b/src/cdk/overlay/position/connected-position-strategy.spec.ts index 0ba80be29cd5..6195ea0ca8b6 100644 --- a/src/cdk/overlay/position/connected-position-strategy.spec.ts +++ b/src/cdk/overlay/position/connected-position-strategy.spec.ts @@ -326,6 +326,28 @@ describe('ConnectedPositionStrategy', () => { expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); }); + it('should allow for the fallback positions to specify their own offsets', () => { + originElement.style.bottom = '0'; + originRect = originElement.getBoundingClientRect(); + strategy = positionBuilder + .connectedTo( + fakeElementRef, + {originX: 'start', originY: 'top'}, + {overlayX: 'start', overlayY: 'top'}) + .withFallbackPosition( + {originX: 'start', originY: 'top'}, + {overlayX: 'start', overlayY: 'bottom'}, + -100, -100); + + strategy.withOffsetY(50).withOffsetY(50); + strategy.attach(fakeOverlayRef(overlayElement)); + strategy.apply(); + + let overlayRect = overlayElement.getBoundingClientRect(); + expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top - 100)); + expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left - 100)); + }); + }); it('should emit onPositionChange event when position changes', () => { diff --git a/src/cdk/overlay/position/connected-position-strategy.ts b/src/cdk/overlay/position/connected-position-strategy.ts index 33f1c290d36a..fb1812aa3f3d 100644 --- a/src/cdk/overlay/position/connected-position-strategy.ts +++ b/src/cdk/overlay/position/connected-position-strategy.ts @@ -187,8 +187,12 @@ export class ConnectedPositionStrategy implements PositionStrategy { */ withFallbackPosition( originPos: OriginConnectionPosition, - overlayPos: OverlayConnectionPosition): this { - this._preferredPositions.push(new ConnectionPositionPair(originPos, overlayPos)); + overlayPos: OverlayConnectionPosition, + offsetX?: number, + offsetY?: number): this { + + const position = new ConnectionPositionPair(originPos, overlayPos, offsetX, offsetY); + this._preferredPositions.push(position); return this; } @@ -291,9 +295,13 @@ export class ConnectedPositionStrategy implements PositionStrategy { overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height; } + // The (x, y) offsets of the overlay based on the current position. + let offsetX = typeof pos.offsetX === 'undefined' ? this._offsetX : pos.offsetX; + let offsetY = typeof pos.offsetY === 'undefined' ? this._offsetY : pos.offsetY; + // The (x, y) coordinates of the overlay. - let x = originPoint.x + overlayStartX + this._offsetX; - let y = originPoint.y + overlayStartY + this._offsetY; + let x = originPoint.x + overlayStartX + offsetX; + let y = originPoint.y + overlayStartY + offsetY; // How much the overlay would overflow at this position, on each side. let leftOverflow = 0 - x; diff --git a/src/cdk/overlay/position/connected-position.ts b/src/cdk/overlay/position/connected-position.ts index e9affad91dba..58ea894b07c1 100644 --- a/src/cdk/overlay/position/connected-position.ts +++ b/src/cdk/overlay/position/connected-position.ts @@ -33,7 +33,12 @@ export class ConnectionPositionPair { overlayX: HorizontalConnectionPos; overlayY: VerticalConnectionPos; - constructor(origin: OriginConnectionPosition, overlay: OverlayConnectionPosition) { + constructor( + origin: OriginConnectionPosition, + overlay: OverlayConnectionPosition, + public offsetX?: number, + public offsetY?: number) { + this.originX = origin.originX; this.originY = origin.originY; this.overlayX = overlay.overlayX; diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index a37c541f3e1e..6f435b8da745 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -348,9 +348,6 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy { // to the edges of the trigger, instead of overlapping it. overlayFallbackX = originX = this.menu.xPosition === 'before' ? 'start' : 'end'; originFallbackX = overlayX = originX === 'end' ? 'start' : 'end'; - - // TODO(crisbeto): this should be a function, once the overlay supports it. - // Right now it will be wrong for the fallback positions. offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : -MENU_PANEL_TOP_PADDING; } else if (!this.menu.overlapTrigger) { originY = overlayY === 'top' ? 'bottom' : 'top'; @@ -366,10 +363,12 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy { {overlayX: overlayFallbackX, overlayY}) .withFallbackPosition( {originX, originY: originFallbackY}, - {overlayX, overlayY: overlayFallbackY}) + {overlayX, overlayY: overlayFallbackY}, + undefined, -offsetY) .withFallbackPosition( {originX: originFallbackX, originY: originFallbackY}, - {overlayX: overlayFallbackX, overlayY: overlayFallbackY}); + {overlayX: overlayFallbackX, overlayY: overlayFallbackY}, + undefined, -offsetY); } /** Cleans up the active subscriptions. */