Skip to content

Commit 074f6ce

Browse files
crisbetoandrewseguin
authored andcommitted
fix(menu): wrong offset for nested menu in a fallback position (#7562)
* 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.
1 parent f0f6a74 commit 074f6ce

File tree

4 files changed

+44
-10
lines changed

4 files changed

+44
-10
lines changed

src/cdk/overlay/position/connected-position-strategy.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,28 @@ describe('ConnectedPositionStrategy', () => {
326326
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
327327
});
328328

329+
it('should allow for the fallback positions to specify their own offsets', () => {
330+
originElement.style.bottom = '0';
331+
originRect = originElement.getBoundingClientRect();
332+
strategy = positionBuilder
333+
.connectedTo(
334+
fakeElementRef,
335+
{originX: 'start', originY: 'top'},
336+
{overlayX: 'start', overlayY: 'top'})
337+
.withFallbackPosition(
338+
{originX: 'start', originY: 'top'},
339+
{overlayX: 'start', overlayY: 'bottom'},
340+
-100, -100);
341+
342+
strategy.withOffsetY(50).withOffsetY(50);
343+
strategy.attach(fakeOverlayRef(overlayElement));
344+
strategy.apply();
345+
346+
let overlayRect = overlayElement.getBoundingClientRect();
347+
expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top - 100));
348+
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left - 100));
349+
});
350+
329351
});
330352

331353
it('should emit onPositionChange event when position changes', () => {

src/cdk/overlay/position/connected-position-strategy.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,12 @@ export class ConnectedPositionStrategy implements PositionStrategy {
192192
*/
193193
withFallbackPosition(
194194
originPos: OriginConnectionPosition,
195-
overlayPos: OverlayConnectionPosition): this {
196-
this._preferredPositions.push(new ConnectionPositionPair(originPos, overlayPos));
195+
overlayPos: OverlayConnectionPosition,
196+
offsetX?: number,
197+
offsetY?: number): this {
198+
199+
const position = new ConnectionPositionPair(originPos, overlayPos, offsetX, offsetY);
200+
this._preferredPositions.push(position);
197201
return this;
198202
}
199203

@@ -296,9 +300,13 @@ export class ConnectedPositionStrategy implements PositionStrategy {
296300
overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height;
297301
}
298302

303+
// The (x, y) offsets of the overlay based on the current position.
304+
let offsetX = typeof pos.offsetX === 'undefined' ? this._offsetX : pos.offsetX;
305+
let offsetY = typeof pos.offsetY === 'undefined' ? this._offsetY : pos.offsetY;
306+
299307
// The (x, y) coordinates of the overlay.
300-
let x = originPoint.x + overlayStartX + this._offsetX;
301-
let y = originPoint.y + overlayStartY + this._offsetY;
308+
let x = originPoint.x + overlayStartX + offsetX;
309+
let y = originPoint.y + overlayStartY + offsetY;
302310

303311
// How much the overlay would overflow at this position, on each side.
304312
let leftOverflow = 0 - x;

src/cdk/overlay/position/connected-position.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ export class ConnectionPositionPair {
3333
overlayX: HorizontalConnectionPos;
3434
overlayY: VerticalConnectionPos;
3535

36-
constructor(origin: OriginConnectionPosition, overlay: OverlayConnectionPosition) {
36+
constructor(
37+
origin: OriginConnectionPosition,
38+
overlay: OverlayConnectionPosition,
39+
public offsetX?: number,
40+
public offsetY?: number) {
41+
3742
this.originX = origin.originX;
3843
this.originY = origin.originY;
3944
this.overlayX = overlay.overlayX;

src/lib/menu/menu-trigger.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,6 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
354354
// to the edges of the trigger, instead of overlapping it.
355355
overlayFallbackX = originX = this.menu.xPosition === 'before' ? 'start' : 'end';
356356
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
357-
358-
// TODO(crisbeto): this should be a function, once the overlay supports it.
359-
// Right now it will be wrong for the fallback positions.
360357
offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : -MENU_PANEL_TOP_PADDING;
361358
} else if (!this.menu.overlapTrigger) {
362359
originY = overlayY === 'top' ? 'bottom' : 'top';
@@ -372,10 +369,12 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
372369
{overlayX: overlayFallbackX, overlayY})
373370
.withFallbackPosition(
374371
{originX, originY: originFallbackY},
375-
{overlayX, overlayY: overlayFallbackY})
372+
{overlayX, overlayY: overlayFallbackY},
373+
undefined, -offsetY)
376374
.withFallbackPosition(
377375
{originX: originFallbackX, originY: originFallbackY},
378-
{overlayX: overlayFallbackX, overlayY: overlayFallbackY});
376+
{overlayX: overlayFallbackX, overlayY: overlayFallbackY},
377+
undefined, -offsetY);
379378
}
380379

381380
/** Cleans up the active subscriptions. */

0 commit comments

Comments
 (0)