diff --git a/ng2-material/all.ts b/ng2-material/all.ts
index 40a0e11d..55b1b612 100644
--- a/ng2-material/all.ts
+++ b/ng2-material/all.ts
@@ -54,13 +54,15 @@ export * from './components/switcher/switch';
import {MdSubheader} from "./components/subheader/subheader";
export * from './components/subheader/subheader';
+import {MdSidenav, SidenavService} from './components/sidenav/sidenav';
+export * from './components/sidenav/sidenav';
+
import {MdToolbar} from './components/toolbar/toolbar';
export * from './components/toolbar/toolbar';
import {MdTabs, MdTab} from './components/tabs/tabs';
import {UrlResolver} from "angular2/compiler";
import {Media} from "./core/util/media";
-export * from './components/toolbar/toolbar';
/**
* Collection of Material Design component directives.
@@ -81,6 +83,7 @@ export const MATERIAL_DIRECTIVES: Type[] = CONST_EXPR([
MdProgressLinear,
MdProgressCircular,
MdRadioButton, MdRadioGroup,
+ MdSidenav,
MdSubheader,
MdSwitch,
MdToolbar,
@@ -93,6 +96,7 @@ export const MATERIAL_DIRECTIVES: Type[] = CONST_EXPR([
export const MATERIAL_PROVIDERS: any[] = [
MdDialog,
Media,
+ SidenavService,
MdRadioDispatcher,
INPUT_VALIDATORS
];
diff --git a/ng2-material/components/sidenav/sidenav.scss b/ng2-material/components/sidenav/sidenav.scss
index 2dca0037..3e88e612 100644
--- a/ng2-material/components/sidenav/sidenav.scss
+++ b/ng2-material/components/sidenav/sidenav.scss
@@ -6,9 +6,9 @@ $sidenav-mobile-width: 320px !default;
$sidenav-desktop-width: 400px !default;
$sidenav-min-space: 56px !default;
-md-sidenav {
+[md-sidenav] {
box-sizing: border-box;
- position: absolute;
+ position: fixed;
flex-direction: column;
z-index: $z-index-sidenav;
@@ -97,13 +97,13 @@ md-sidenav {
}
@media screen and (min-width: $layout-breakpoint-sm) {
- md-sidenav {
+ [md-sidenav] {
max-width: $sidenav-desktop-width;
}
}
@media screen and (max-width: $sidenav-desktop-width + $sidenav-min-space) {
- md-sidenav {
+ [md-sidenav] {
width: calc(100% - #{$sidenav-min-space});
min-width: calc(100% - #{$sidenav-min-space});
max-width: calc(100% - #{$sidenav-min-space});
@@ -120,6 +120,6 @@ md-sidenav {
}
-md-sidenav {
- background-color: md-color($md-background,500); //'{{background-color}}';
+[md-sidenav] {
+ background-color: md-color($md-background,A100); //'{{background-color}}';
}
diff --git a/ng2-material/components/sidenav/sidenav.ts b/ng2-material/components/sidenav/sidenav.ts
index 6ca3cf66..b58727ef 100644
--- a/ng2-material/components/sidenav/sidenav.ts
+++ b/ng2-material/components/sidenav/sidenav.ts
@@ -1,7 +1,166 @@
-import {Directive} from 'angular2/core';
+import {Directive, Injectable} from 'angular2/core';
import {Input} from 'angular2/core';
+import {Animate} from "../../core/util/animate";
+import {MdDialog} from "../dialog/dialog";
+import {MdDialogRef} from "../dialog/dialog_ref";
+import {Media} from "../../core/util/media";
+import {MdBackdrop} from "../backdrop/backdrop";
+import {ElementRef} from "angular2/core";
+import {ContentChildren} from "angular2/core";
+import {OnDestroy} from "angular2/core";
+import {AfterContentInit} from "angular2/core";
+import {OnInit} from "angular2/core";
+import {QueryList} from "angular2/core";
+import {Component} from "angular2/core";
+import {ViewChild} from "angular2/core";
+import {ComponentRef} from "angular2/core";
+import {Renderer} from "angular2/core";
+import {DynamicComponentLoader} from "angular2/core";
+import {DOM} from "angular2/src/platform/dom/dom_adapter";
+import {Attribute} from "angular2/core";
+import {isPresent} from "angular2/src/facade/lang";
+import {forwardRef} from "angular2/core";
+import {Inject} from "angular2/core";
-@Directive({selector: 'md-sidenav'})
-export class MdSidenav {
- @Input() public opened: boolean = true;
+// TODO(jd): Lock open sidenav
+// TODO(jd): Right alignment
+// TODO(jd): Behaviors for testing
+// - can lock open
+// - service can find sidenavs by name
+
+
+/**
+ * A slide-out navigation element that transitions in from the left or right.
+ *
+ * ```html
+ *
+ * ```
+ */
+@Directive({
+ selector: '[md-sidenav]'
+})
+export class MdSidenav extends MdBackdrop implements OnInit, OnDestroy {
+ @Input('md-sidenav')
+ name: string = 'default';
+
+ private _backdropRef: ComponentRef = null;
+
+ private _defaultContainer = DOM.query('body');
+
+ transitionClass: string = 'md-closed';
+ transitionAddClass = false;
+
+ constructor(public element: ElementRef,
+ public dcl: DynamicComponentLoader,
+ public renderer: Renderer,
+ @Inject(forwardRef(() => SidenavService)) public service: SidenavService,
+ @Attribute('md-sidenav') name: string) {
+ super(element);
+ DOM.addClass(this.element.nativeElement, this.transitionClass);
+ if (isPresent(name)) {
+ this.name = name;
+ }
+ }
+
+ show(): Promise {
+ return this._createBackdrop(this.element).then(() => super.show());
+ }
+
+ _destroyBackdrop(): Promise {
+ if (this._backdropRef) {
+ this._backdropRef.dispose();
+ this._backdropRef = null;
+ }
+ return Promise.resolve();
+ }
+
+
+ toggle(): Promise {
+ return this.visible ? this.hide() : this.show();
+ }
+
+ isOpen(): boolean {
+ return this.visible;
+ }
+
+ private _lockedOpen: boolean = false;
+
+ isLockedOpen(): boolean {
+ return this._lockedOpen;
+ }
+
+
+ _createBackdrop(elementRef: ElementRef): Promise {
+ if (this._backdropRef) {
+ return Promise.resolve(this._backdropRef);
+ }
+ return this.dcl.loadNextToLocation(MdBackdrop, elementRef)
+ .then((componentRef) => {
+ let backdrop: MdBackdrop = componentRef.instance;
+ backdrop.clickClose = true;
+ this.renderer.setElementClass(componentRef.location, 'md-backdrop', true);
+ this.renderer.setElementClass(componentRef.location, 'md-opaque', true);
+ DOM.appendChild(this._defaultContainer, componentRef.location.nativeElement);
+
+ this._backdropRef = componentRef;
+ // When this component is shown/hidden, sync the backdrop
+ this.onShowing.subscribe(() => backdrop.show());
+ this.onHiding.subscribe(() => backdrop.hide());
+ // If the sidenav is hidden, release the backdrop
+ this.onHidden.subscribe(() => this._destroyBackdrop());
+ // If the backdrop is hidden, hide the nav
+ backdrop.onHiding.subscribe(() => this.hide());
+
+ return componentRef;
+ });
+ }
+
+ ngOnInit(): any {
+ this.service.register(this);
+ }
+
+ ngOnDestroy(): any {
+ this.service.unregister(this);
+ }
+
+}
+
+@Injectable()
+export class SidenavService {
+ private _instances: MdSidenav[] = [];
+
+ register(instance: MdSidenav) {
+ this._instances.push(instance);
+ }
+
+ unregister(instance: MdSidenav) {
+ this._instances = this._instances.filter((c: MdSidenav) => {
+ return c.name !== instance.name;
+ });
+ }
+
+ show(name: string): Promise {
+ let instance: MdSidenav = this._findInstance(name);
+ if (!instance) {
+ return Promise.reject('invalid container');
+ }
+ return instance.show();
+ }
+
+ hide(name: string): Promise {
+ let instance: MdSidenav = this._findInstance(name);
+ if (!instance) {
+ return Promise.reject('invalid container');
+ }
+ return instance.hide();
+ }
+
+ private _findInstance(name: string): MdSidenav {
+ return this._instances.filter((c: MdSidenav) => {
+ return c.name === name;
+ })[0];
+ }
}