Skip to content

Commit

Permalink
fix(menu): reposition menu if it would open off screen (#1761)
Browse files Browse the repository at this point in the history
  • Loading branch information
kara authored Nov 16, 2016
1 parent 522324c commit 7572e34
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 42 deletions.
14 changes: 7 additions & 7 deletions src/lib/menu/menu-directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {
@Attribute('y-position') posY: MenuPositionY) {
if (posX) { this._setPositionX(posX); }
if (posY) { this._setPositionY(posY); }
this._setPositionClasses();
this.setPositionClasses(this.positionX, this.positionY);
}

// TODO: internal
Expand Down Expand Up @@ -83,7 +83,7 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {
obj[className] = true;
return obj;
}, {});
this._setPositionClasses();
this.setPositionClasses(this.positionX, this.positionY);
}

@Output() close = new EventEmitter<void>();
Expand Down Expand Up @@ -123,11 +123,11 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {
* It's necessary to set position-based classes to ensure the menu panel animation
* folds out from the correct direction.
*/
private _setPositionClasses() {
this._classList['md-menu-before'] = this.positionX == 'before';
this._classList['md-menu-after'] = this.positionX == 'after';
this._classList['md-menu-above'] = this.positionY == 'above';
this._classList['md-menu-below'] = this.positionY == 'below';
setPositionClasses(posX: MenuPositionX, posY: MenuPositionY): void {
this._classList['md-menu-before'] = posX == 'before';
this._classList['md-menu-after'] = posX == 'after';
this._classList['md-menu-above'] = posY == 'above';
this._classList['md-menu-below'] = posY == 'below';
}

}
1 change: 1 addition & 0 deletions src/lib/menu/menu-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export interface MdMenuPanel {
templateRef: TemplateRef<any>;
close: EventEmitter<void>;
focusFirstItem: () => void;
setPositionClasses: (x: MenuPositionX, y: MenuPositionY) => void;
}
60 changes: 47 additions & 13 deletions src/lib/menu/menu-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import {
TemplatePortal,
ConnectedPositionStrategy,
HorizontalConnectionPos,
VerticalConnectionPos
VerticalConnectionPos,
} from '../core';
import { Subscription } from 'rxjs/Subscription';
import {MenuPositionX, MenuPositionY} from './menu-positions';

/**
* This directive is intended to be used in conjunction with an md-menu tag. It is
Expand All @@ -44,6 +45,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
private _overlayRef: OverlayRef;
private _menuOpen: boolean = false;
private _backdropSubscription: Subscription;
private _positionSubscription: Subscription;

// tracking input type is necessary so it's possible to only auto-focus
// the first item of the list when the menu is opened via the keyboard
Expand Down Expand Up @@ -92,9 +94,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
this._overlayRef.dispose();
this._overlayRef = null;

if (this._backdropSubscription) {
this._backdropSubscription.unsubscribe();
}
this._cleanUpSubscriptions();
}
}

Expand Down Expand Up @@ -172,7 +172,9 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
private _createOverlay(): void {
if (!this._overlayRef) {
this._portal = new TemplatePortal(this.menu.templateRef, this._viewContainerRef);
this._overlayRef = this._overlay.create(this._getOverlayConfig());
const config = this._getOverlayConfig();
this._subscribeToPositions(config.positionStrategy as ConnectedPositionStrategy);
this._overlayRef = this._overlay.create(config);
}
}

Expand All @@ -190,20 +192,52 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
return overlayState;
}

/**
* Listens to changes in the position of the overlay and sets the correct classes
* on the menu based on the new position. This ensures the animation origin is always
* correct, even if a fallback position is used for the overlay.
*/
private _subscribeToPositions(position: ConnectedPositionStrategy): void {
this._positionSubscription = position.onPositionChange.subscribe((change) => {
const posX: MenuPositionX = change.connectionPair.originX === 'start' ? 'after' : 'before';
const posY: MenuPositionY = change.connectionPair.originY === 'top' ? 'below' : 'above';
this.menu.setPositionClasses(posX, posY);
});
}

/**
* This method builds the position strategy for the overlay, so the menu is properly connected
* to the trigger.
* @returns ConnectedPositionStrategy
*/
private _getPosition(): ConnectedPositionStrategy {
const positionX: HorizontalConnectionPos = this.menu.positionX === 'before' ? 'end' : 'start';
const positionY: VerticalConnectionPos = this.menu.positionY === 'above' ? 'bottom' : 'top';

return this._overlay.position().connectedTo(
this._element,
{originX: positionX, originY: positionY},
{overlayX: positionX, overlayY: positionY}
);
const [posX, fallbackX]: HorizontalConnectionPos[] =
this.menu.positionX === 'before' ? ['end', 'start'] : ['start', 'end'];

const [posY, fallbackY]: VerticalConnectionPos[] =
this.menu.positionY === 'above' ? ['bottom', 'top'] : ['top', 'bottom'];

return this._overlay.position()
.connectedTo(this._element,
{originX: posX, originY: posY}, {overlayX: posX, overlayY: posY})
.withFallbackPosition(
{originX: fallbackX, originY: posY},
{overlayX: fallbackX, overlayY: posY})
.withFallbackPosition(
{originX: posX, originY: fallbackY},
{overlayX: posX, overlayY: fallbackY})
.withFallbackPosition(
{originX: fallbackX, originY: fallbackY},
{overlayX: fallbackX, overlayY: fallbackY});
}

private _cleanUpSubscriptions(): void {
if (this._backdropSubscription) {
this._backdropSubscription.unsubscribe();
}
if (this._positionSubscription) {
this._positionSubscription.unsubscribe();
}
}

_handleMousedown(event: MouseEvent): void {
Expand Down
Loading

0 comments on commit 7572e34

Please sign in to comment.