From fea592345441296cc3d0a37ae6663176dc7847e3 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Fri, 29 Apr 2016 15:48:40 -0700 Subject: [PATCH] fix(sidenav): prevent content from scrolling when sidenav is open --- src/components/sidenav/sidenav.scss | 7 +++++ src/components/sidenav/sidenav.ts | 40 ++++++++++++++++++++++------- src/demo-app/demo-app.scss | 9 +++---- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/components/sidenav/sidenav.scss b/src/components/sidenav/sidenav.scss index 18e38aee1784..8e9a710faa81 100644 --- a/src/components/sidenav/sidenav.scss +++ b/src/components/sidenav/sidenav.scss @@ -64,6 +64,10 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; // TODO(hansl): Update this with a more robust solution. &[fullscreen] { @include md-sidenav-fullscreen(); + + &.md-sidenav-opened { + overflow: hidden; + } } // Need this to take up space in the layout. @@ -109,6 +113,9 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; z-index: 3; min-width: 5%; + // TODO(kara): revisit scrolling behavior for sidenavs + overflow-y: auto; + background-color: $md-sidenav-background-color; @include md-sidenav-transition(0, -100%); diff --git a/src/components/sidenav/sidenav.ts b/src/components/sidenav/sidenav.ts index 28a50ac25dda..93d72fa3f1f2 100644 --- a/src/components/sidenav/sidenav.ts +++ b/src/components/sidenav/sidenav.ts @@ -13,6 +13,7 @@ import { Type, ChangeDetectionStrategy, EventEmitter, + Renderer } from 'angular2/core'; import {BaseException} from 'angular2/src/facade/exceptions'; import {Dir} from '../../core/rtl/dir'; @@ -49,7 +50,7 @@ export class MdSidenav { @Input() mode: 'over' | 'push' | 'side' = 'over'; /** Whether the sidenav is opened. */ - @Input('opened') private _opened: boolean; + @Input('opened') private _opened: boolean = false; /** Event emitted when the sidenav is being opened. Use this to synchronize animations. */ @Output('open-start') onOpenStart = new EventEmitter(); @@ -247,7 +248,8 @@ export class MdSidenavLayout implements AfterContentInit { get start() { return this._start; } get end() { return this._end; } - constructor(@Optional() @Host() private _dir: Dir) { + constructor(@Optional() @Host() private _dir: Dir, private _element: ElementRef, + private _renderer: Renderer) { // If a `Dir` directive exists up the tree, listen direction changes and update the left/right // properties to point to the proper start/end. if (_dir != null) { @@ -258,9 +260,28 @@ export class MdSidenavLayout implements AfterContentInit { ngAfterContentInit() { // On changes, assert on consistency. this._sidenavs.changes.subscribe(() => this._validateDrawers()); + this._sidenavs.forEach((sidenav: MdSidenav) => this._watchSidenavToggle(sidenav)); this._validateDrawers(); } + /* + * Subscribes to sidenav events in order to set a class on the main layout element when the sidenav + * is open and the backdrop is visible. This ensures any overflow on the layout element is properly + * hidden. + * */ + private _watchSidenavToggle(sidenav: MdSidenav): void { + if (!sidenav || sidenav.mode === 'side') { return; } + sidenav.onOpen.subscribe(() => this._setLayoutClass(sidenav, true)); + sidenav.onClose.subscribe(() => this._setLayoutClass(sidenav, false)); + } + + /* + * Toggles the 'md-sidenav-opened' class on the main 'md-sidenav-layout' element. + * */ + private _setLayoutClass(sidenav: MdSidenav, bool: boolean): void { + this._renderer.setElementClass(this._element.nativeElement, 'md-sidenav-opened', bool); + } + /** The sidenav at the start/end alignment, independent of direction. */ private _start: MdSidenav; @@ -318,9 +339,13 @@ export class MdSidenavLayout implements AfterContentInit { } } - private _isShowingBackdrop() { - return (this._start != null && this._start.mode != 'side' && this._start.opened) - || (this._end != null && this._end.mode != 'side' && this._end.opened); + private _isShowingBackdrop(): boolean { + return (this._isSidenavOpen(this._start) && this._start.mode != 'side') + || (this._isSidenavOpen(this._end) && this._end.mode != 'side'); + } + + private _isSidenavOpen(side: MdSidenav): boolean { + return side != null && side.opened; } /** @@ -330,10 +355,7 @@ export class MdSidenavLayout implements AfterContentInit { * @private */ private _getSidenavEffectiveWidth(sidenav: MdSidenav, mode: string): number { - if (sidenav != null && sidenav.mode == mode && sidenav.opened) { - return sidenav._width; - } - return 0; + return (this._isSidenavOpen(sidenav) && sidenav.mode == mode) ? sidenav._width : 0; } private _getMarginLeft() { diff --git a/src/demo-app/demo-app.scss b/src/demo-app/demo-app.scss index bf3e1f9263a1..eec56e9018bf 100644 --- a/src/demo-app/demo-app.scss +++ b/src/demo-app/demo-app.scss @@ -9,13 +9,13 @@ } md-sidenav { - width: 15%; + min-width: 15%; [md-button] { width: 100%; - position: absolute; + position: relative; bottom: 0; - margin-bottom: 24px; + margin: 24px 0; } } @@ -39,5 +39,4 @@ h1 { font-size: 20px; } -} - +} \ No newline at end of file