diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts
index 26d96a4ee0e0..f1f9fa5cd178 100644
--- a/src/demo-app/demo-app/demo-app.ts
+++ b/src/demo-app/demo-app/demo-app.ts
@@ -1,10 +1,5 @@
-import {
- Component,
- ViewEncapsulation,
- ElementRef,
- Renderer2,
-} from '@angular/core';
import {OverlayContainer} from '@angular/cdk/overlay';
+import {Component, ElementRef, Renderer2, ViewEncapsulation} from '@angular/core';
/**
* The entry app for demo site. Routes under `accessibility` will use AccessibilityDemo component,
@@ -48,22 +43,25 @@ export class DemoApp {
dark = false;
navItems = [
{name: 'Autocomplete', route: '/autocomplete'},
- {name: 'Button', route: '/button'},
{name: 'Button Toggle', route: '/button-toggle'},
+ {name: 'Button', route: '/button'},
{name: 'Card', route: '/card'},
- {name: 'Chips', route: '/chips'},
{name: 'Checkbox', route: '/checkbox'},
+ {name: 'Chips', route: '/chips'},
{name: 'Datepicker', route: '/datepicker'},
{name: 'Dialog', route: '/dialog'},
+ {name: 'Drawer', route: '/drawer'},
{name: 'Expansion Panel', route: '/expansion'},
+ {name: 'Focus Origin', route: '/focus-origin'},
{name: 'Gestures', route: '/gestures'},
{name: 'Grid List', route: '/grid-list'},
{name: 'Icon', route: '/icon'},
{name: 'Input', route: '/input'},
{name: 'List', route: '/list'},
- {name: 'Menu', route: '/menu'},
{name: 'Live Announcer', route: '/live-announcer'},
+ {name: 'Menu', route: '/menu'},
{name: 'Overlay', route: '/overlay'},
+ {name: 'Platform', route: '/platform'},
{name: 'Portal', route: '/portal'},
{name: 'Progress Bar', route: '/progress-bar'},
{name: 'Progress Spinner', route: '/progress-spinner'},
@@ -71,16 +69,15 @@ export class DemoApp {
{name: 'Ripple', route: '/ripple'},
{name: 'Select', route: '/select'},
{name: 'Sidenav', route: '/sidenav'},
- {name: 'Slider', route: '/slider'},
{name: 'Slide Toggle', route: '/slide-toggle'},
+ {name: 'Slider', route: '/slider'},
{name: 'Snack Bar', route: '/snack-bar'},
{name: 'Stepper', route: '/stepper'},
+ {name: 'Style', route: '/style'},
{name: 'Table', route: '/table'},
{name: 'Tabs', route: '/tabs'},
{name: 'Toolbar', route: '/toolbar'},
{name: 'Tooltip', route: '/tooltip'},
- {name: 'Platform', route: '/platform'},
- {name: 'Focus Origin', route: '/focus-origin'},
{name: 'Typography', route: '/typography'}
];
diff --git a/src/demo-app/demo-app/demo-module.ts b/src/demo-app/demo-app/demo-module.ts
index 59a632277ee1..80e21dbe5c26 100644
--- a/src/demo-app/demo-app/demo-module.ts
+++ b/src/demo-app/demo-app/demo-module.ts
@@ -1,52 +1,50 @@
-import {NgModule} from '@angular/core';
+import {FullscreenOverlayContainer, OverlayContainer} from '@angular/cdk/overlay';
import {CommonModule} from '@angular/common';
+import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {RouterModule} from '@angular/router';
-import {DemoApp, Home} from './demo-app';
-import {DEMO_APP_ROUTES} from './routes';
-import {ProgressBarDemo} from '../progress-bar/progress-bar-demo';
-import {ContentElementDialog, DialogDemo, IFrameDialog, JazzDialog} from '../dialog/dialog-demo';
-import {RippleDemo} from '../ripple/ripple-demo';
-import {IconDemo} from '../icon/icon-demo';
-import {GesturesDemo} from '../gestures/gestures-demo';
+import {AutocompleteDemo} from '../autocomplete/autocomplete-demo';
+import {BaselineDemo} from '../baseline/baseline-demo';
+import {ButtonToggleDemo} from '../button-toggle/button-toggle-demo';
+import {ButtonDemo} from '../button/button-demo';
import {CardDemo} from '../card/card-demo';
+import {CheckboxDemo, MdCheckboxDemoNestedChecklist} from '../checkbox/checkbox-demo';
import {ChipsDemo} from '../chips/chips-demo';
-import {RadioDemo} from '../radio/radio-demo';
-import {ButtonToggleDemo} from '../button-toggle/button-toggle-demo';
-import {ProgressSpinnerDemo} from '../progress-spinner/progress-spinner-demo';
-import {TooltipDemo} from '../tooltip/tooltip-demo';
-import {ListDemo} from '../list/list-demo';
-import {BaselineDemo} from '../baseline/baseline-demo';
+import {DatepickerDemo} from '../datepicker/datepicker-demo';
+import {DemoMaterialModule} from '../demo-material-module';
+import {ContentElementDialog, DialogDemo, IFrameDialog, JazzDialog} from '../dialog/dialog-demo';
+import {DrawerDemo} from '../drawer/drawer-demo';
+import {ExpansionDemo} from '../expansion/expansion-demo';
+import {FocusOriginDemo} from '../focus-origin/focus-origin-demo';
+import {GesturesDemo} from '../gestures/gestures-demo';
import {GridListDemo} from '../grid-list/grid-list-demo';
+import {IconDemo} from '../icon/icon-demo';
+import {InputDemo} from '../input/input-demo';
+import {ListDemo} from '../list/list-demo';
import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo';
+import {MenuDemo} from '../menu/menu-demo';
import {OverlayDemo, RotiniPanel, SpagettiPanel} from '../overlay/overlay-demo';
-import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo';
-import {ToolbarDemo} from '../toolbar/toolbar-demo';
-import {ButtonDemo} from '../button/button-demo';
-import {CheckboxDemo, MdCheckboxDemoNestedChecklist} from '../checkbox/checkbox-demo';
+import {PlatformDemo} from '../platform/platform-demo';
+import {PortalDemo, ScienceJoke} from '../portal/portal-demo';
+import {ProgressBarDemo} from '../progress-bar/progress-bar-demo';
+import {ProgressSpinnerDemo} from '../progress-spinner/progress-spinner-demo';
+import {RadioDemo} from '../radio/radio-demo';
+import {RippleDemo} from '../ripple/ripple-demo';
import {SelectDemo} from '../select/select-demo';
-import {SliderDemo} from '../slider/slider-demo';
import {SidenavDemo} from '../sidenav/sidenav-demo';
+import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo';
+import {SliderDemo} from '../slider/slider-demo';
import {SnackBarDemo} from '../snack-bar/snack-bar-demo';
-import {PortalDemo, ScienceJoke} from '../portal/portal-demo';
-import {MenuDemo} from '../menu/menu-demo';
-import {FoggyTabContent, RainyTabContent, SunnyTabContent, TabsDemo} from '../tabs/tabs-demo';
-import {PlatformDemo} from '../platform/platform-demo';
-import {AutocompleteDemo} from '../autocomplete/autocomplete-demo';
-import {InputDemo} from '../input/input-demo';
-import {FocusOriginDemo} from '../focus-origin/focus-origin-demo';
-import {TableDemo} from '../table/table-demo';
-import {PeopleDatabase} from '../table/people-database';
-import {DatepickerDemo} from '../datepicker/datepicker-demo';
-import {TypographyDemo} from '../typography/typography-demo';
-import {ExpansionDemo} from '../expansion/expansion-demo';
import {StepperDemo} from '../stepper/stepper-demo';
-import {DemoMaterialModule} from '../demo-material-module';
-import {
- FullscreenOverlayContainer,
- OverlayContainer,
-} from '@angular/cdk/overlay';
+import {PeopleDatabase} from '../table/people-database';
+import {TableDemo} from '../table/table-demo';
import {TableHeaderDemo} from '../table/table-header-demo';
+import {FoggyTabContent, RainyTabContent, SunnyTabContent, TabsDemo} from '../tabs/tabs-demo';
+import {ToolbarDemo} from '../toolbar/toolbar-demo';
+import {TooltipDemo} from '../tooltip/tooltip-demo';
+import {TypographyDemo} from '../typography/typography-demo';
+import {DemoApp, Home} from './demo-app';
+import {DEMO_APP_ROUTES} from './routes';
@NgModule({
imports: [
@@ -62,29 +60,34 @@ import {TableHeaderDemo} from '../table/table-header-demo';
ButtonDemo,
ButtonToggleDemo,
CardDemo,
- ChipsDemo,
CheckboxDemo,
+ ChipsDemo,
+ ContentElementDialog,
DatepickerDemo,
DemoApp,
DialogDemo,
+ DrawerDemo,
+ ExpansionDemo,
+ FocusOriginDemo,
+ FoggyTabContent,
GesturesDemo,
GridListDemo,
Home,
IconDemo,
+ IFrameDialog,
InputDemo,
JazzDialog,
- ContentElementDialog,
- IFrameDialog,
ListDemo,
LiveAnnouncerDemo,
MdCheckboxDemoNestedChecklist,
MenuDemo,
- SnackBarDemo,
OverlayDemo,
+ PlatformDemo,
PortalDemo,
ProgressBarDemo,
ProgressSpinnerDemo,
RadioDemo,
+ RainyTabContent,
RippleDemo,
RotiniPanel,
ScienceJoke,
@@ -92,30 +95,26 @@ import {TableHeaderDemo} from '../table/table-header-demo';
SidenavDemo,
SliderDemo,
SlideToggleDemo,
+ SnackBarDemo,
SpagettiPanel,
StepperDemo,
- FocusOriginDemo,
+ SunnyTabContent,
+ TableDemo,
TableHeaderDemo,
+ TabsDemo,
ToolbarDemo,
TooltipDemo,
- TableDemo,
- TabsDemo,
- SunnyTabContent,
- RainyTabContent,
- FoggyTabContent,
- PlatformDemo,
TypographyDemo,
- ExpansionDemo,
],
providers: [
{provide: OverlayContainer, useClass: FullscreenOverlayContainer},
PeopleDatabase
],
entryComponents: [
- DemoApp,
- JazzDialog,
ContentElementDialog,
+ DemoApp,
IFrameDialog,
+ JazzDialog,
RotiniPanel,
ScienceJoke,
SpagettiPanel,
diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts
index 7ee134565fdb..cebe53bdb29e 100644
--- a/src/demo-app/demo-app/routes.ts
+++ b/src/demo-app/demo-app/routes.ts
@@ -1,85 +1,86 @@
import {Routes} from '@angular/router';
-import {Home} from './demo-app';
-import {ButtonDemo} from '../button/button-demo';
+import {AccessibilityDemo} from '../a11y/a11y';
+import {ACCESSIBILITY_DEMO_ROUTES} from '../a11y/routes';
+import {AutocompleteDemo} from '../autocomplete/autocomplete-demo';
import {BaselineDemo} from '../baseline/baseline-demo';
import {ButtonToggleDemo} from '../button-toggle/button-toggle-demo';
-import {TabsDemo} from '../tabs/tabs-demo';
-import {GridListDemo} from '../grid-list/grid-list-demo';
+import {ButtonDemo} from '../button/button-demo';
+import {CardDemo} from '../card/card-demo';
+import {CheckboxDemo} from '../checkbox/checkbox-demo';
+import {ChipsDemo} from '../chips/chips-demo';
+import {DatepickerDemo} from '../datepicker/datepicker-demo';
+import {DialogDemo} from '../dialog/dialog-demo';
+import {DrawerDemo} from '../drawer/drawer-demo';
+import {ExpansionDemo} from '../expansion/expansion-demo';
+import {FocusOriginDemo} from '../focus-origin/focus-origin-demo';
import {GesturesDemo} from '../gestures/gestures-demo';
-import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo';
-import {ListDemo} from '../list/list-demo';
+import {GridListDemo} from '../grid-list/grid-list-demo';
import {IconDemo} from '../icon/icon-demo';
-import {ToolbarDemo} from '../toolbar/toolbar-demo';
-import {CheckboxDemo} from '../checkbox/checkbox-demo';
+import {InputDemo} from '../input/input-demo';
+import {ListDemo} from '../list/list-demo';
+import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo';
+import {MenuDemo} from '../menu/menu-demo';
import {OverlayDemo} from '../overlay/overlay-demo';
+import {PlatformDemo} from '../platform/platform-demo';
import {PortalDemo} from '../portal/portal-demo';
import {ProgressBarDemo} from '../progress-bar/progress-bar-demo';
import {ProgressSpinnerDemo} from '../progress-spinner/progress-spinner-demo';
+import {RadioDemo} from '../radio/radio-demo';
+import {RippleDemo} from '../ripple/ripple-demo';
import {SelectDemo} from '../select/select-demo';
import {SidenavDemo} from '../sidenav/sidenav-demo';
import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo';
import {SliderDemo} from '../slider/slider-demo';
-import {RadioDemo} from '../radio/radio-demo';
-import {CardDemo} from '../card/card-demo';
-import {ChipsDemo} from '../chips/chips-demo';
-import {MenuDemo} from '../menu/menu-demo';
-import {RippleDemo} from '../ripple/ripple-demo';
-import {DialogDemo} from '../dialog/dialog-demo';
-import {TooltipDemo} from '../tooltip/tooltip-demo';
import {SnackBarDemo} from '../snack-bar/snack-bar-demo';
-import {TABS_DEMO_ROUTES} from '../tabs/routes';
-import {PlatformDemo} from '../platform/platform-demo';
-import {AutocompleteDemo} from '../autocomplete/autocomplete-demo';
-import {InputDemo} from '../input/input-demo';
-import {FocusOriginDemo} from '../focus-origin/focus-origin-demo';
-import {DatepickerDemo} from '../datepicker/datepicker-demo';
+import {StepperDemo} from '../stepper/stepper-demo';
import {TableDemo} from '../table/table-demo';
+import {TABS_DEMO_ROUTES} from '../tabs/routes';
+import {TabsDemo} from '../tabs/tabs-demo';
+import {ToolbarDemo} from '../toolbar/toolbar-demo';
+import {TooltipDemo} from '../tooltip/tooltip-demo';
import {TypographyDemo} from '../typography/typography-demo';
-import {ExpansionDemo} from '../expansion/expansion-demo';
-import {StepperDemo} from '../stepper/stepper-demo';
-import {DemoApp} from './demo-app';
-import {AccessibilityDemo} from '../a11y/a11y';
-import {ACCESSIBILITY_DEMO_ROUTES} from '../a11y/routes';
+import {DemoApp, Home} from './demo-app';
export const DEMO_APP_ROUTES: Routes = [
{path: '', component: DemoApp, children: [
{path: '', component: Home},
{path: 'autocomplete', component: AutocompleteDemo},
+ {path: 'baseline', component: BaselineDemo},
{path: 'button', component: ButtonDemo},
+ {path: 'button-toggle', component: ButtonToggleDemo},
{path: 'card', component: CardDemo},
+ {path: 'checkbox', component: CheckboxDemo},
{path: 'chips', component: ChipsDemo},
- {path: 'table', component: TableDemo},
{path: 'datepicker', component: DatepickerDemo},
+ {path: 'dialog', component: DialogDemo},
+ {path: 'drawer', component: DrawerDemo},
+ {path: 'expansion', component: ExpansionDemo},
+ {path: 'focus-origin', component: FocusOriginDemo},
+ {path: 'gestures', component: GesturesDemo},
+ {path: 'grid-list', component: GridListDemo},
+ {path: 'icon', component: IconDemo},
+ {path: 'input', component: InputDemo},
+ {path: 'list', component: ListDemo},
+ {path: 'live-announcer', component: LiveAnnouncerDemo},
+ {path: 'menu', component: MenuDemo},
+ {path: 'overlay', component: OverlayDemo},
+ {path: 'platform', component: PlatformDemo},
+ {path: 'portal', component: PortalDemo},
+ {path: 'progress-bar', component: ProgressBarDemo},
+ {path: 'progress-spinner', component: ProgressSpinnerDemo},
{path: 'radio', component: RadioDemo},
+ {path: 'ripple', component: RippleDemo},
{path: 'select', component: SelectDemo},
{path: 'sidenav', component: SidenavDemo},
{path: 'slide-toggle', component: SlideToggleDemo},
{path: 'slider', component: SliderDemo},
- {path: 'progress-spinner', component: ProgressSpinnerDemo},
- {path: 'progress-bar', component: ProgressBarDemo},
- {path: 'portal', component: PortalDemo},
- {path: 'overlay', component: OverlayDemo},
- {path: 'checkbox', component: CheckboxDemo},
- {path: 'input', component: InputDemo},
- {path: 'toolbar', component: ToolbarDemo},
- {path: 'icon', component: IconDemo},
- {path: 'list', component: ListDemo},
- {path: 'menu', component: MenuDemo},
- {path: 'live-announcer', component: LiveAnnouncerDemo},
- {path: 'gestures', component: GesturesDemo},
- {path: 'grid-list', component: GridListDemo},
+ {path: 'snack-bar', component: SnackBarDemo},
+ {path: 'stepper', component: StepperDemo},
+ {path: 'table', component: TableDemo},
{path: 'tabs', component: TabsDemo, children: TABS_DEMO_ROUTES},
- {path: 'button-toggle', component: ButtonToggleDemo},
- {path: 'baseline', component: BaselineDemo},
- {path: 'ripple', component: RippleDemo},
- {path: 'dialog', component: DialogDemo},
+ {path: 'toolbar', component: ToolbarDemo},
{path: 'tooltip', component: TooltipDemo},
- {path: 'snack-bar', component: SnackBarDemo},
- {path: 'platform', component: PlatformDemo},
- {path: 'focus-origin', component: FocusOriginDemo},
{path: 'typography', component: TypographyDemo},
- {path: 'expansion', component: ExpansionDemo},
- {path: 'stepper', component: StepperDemo}
]}
];
diff --git a/src/demo-app/drawer/drawer-demo.html b/src/demo-app/drawer/drawer-demo.html
new file mode 100644
index 000000000000..3a2dddc4d1d2
--- /dev/null
+++ b/src/demo-app/drawer/drawer-demo.html
@@ -0,0 +1,87 @@
+
Basic Use Case
+
+
+
+ Start Side Drawer
+
+
+
+
+
+
+ Mode: {{start.mode}}
+
+
+
+
+
+ End Side Drawer
+
+
+
+
+
+
My Content
+
+
+
+
+
+
+
+
+
+
+
+
+
+Drawer Already Opened
+
+
+
+ Drawer
+
+
+
+
+
+
+
+Dynamic Position Drawer
+
+
+ Start
+ End
+
+
+
+
+
+
+
+Drawer with focus attributes
+
+
+
+
+ Link
+ Focus region start
+ Link
+ Initially focused
+ Focus region end
+ Link
+
+
+
+
+
My Content
+
+
+
+
+
+
+
diff --git a/src/demo-app/drawer/drawer-demo.scss b/src/demo-app/drawer/drawer-demo.scss
new file mode 100644
index 000000000000..1bf747c0615b
--- /dev/null
+++ b/src/demo-app/drawer/drawer-demo.scss
@@ -0,0 +1,7 @@
+.demo-drawer-container {
+ border: 3px solid black;
+}
+
+.demo-drawer-content {
+ padding: 15px;
+}
diff --git a/src/demo-app/drawer/drawer-demo.ts b/src/demo-app/drawer/drawer-demo.ts
new file mode 100644
index 000000000000..87a740988f7e
--- /dev/null
+++ b/src/demo-app/drawer/drawer-demo.ts
@@ -0,0 +1,14 @@
+import {Component, ViewEncapsulation} from '@angular/core';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'drawer-demo',
+ templateUrl: 'drawer-demo.html',
+ styleUrls: ['drawer-demo.css'],
+ encapsulation: ViewEncapsulation.None,
+ preserveWhitespaces: false,
+})
+export class DrawerDemo {
+ invert = false;
+}
diff --git a/src/demo-app/sidenav/sidenav-demo.html b/src/demo-app/sidenav/sidenav-demo.html
index bdddd69c381a..fdc13d483172 100644
--- a/src/demo-app/sidenav/sidenav-demo.html
+++ b/src/demo-app/sidenav/sidenav-demo.html
@@ -1,87 +1,60 @@
-Basic Use Case
-
-
-
- Start Side Drawer
-
-
-
-
-
-
- Mode: {{start.mode}}
-
-
-
-
-
- End Side Drawer
-
-
-
-
-
-
My Content
-
-
-
-
-
-
-
-
-
-
-
-
-
-Sidenav Already Opened
-
-
-
- Drawer
-
-
-
-
-
-
-
-Dynamic Position Sidenav
-
-
- Start
- End
-
-
-
-
-
-
-
-Sidenav with focus attributes
-
-
-
-
- Link
- Focus region start
- Link
- Initially focused
- Focus region end
- Link
-
-
-
-
-
My Content
-
-
-
-
-
-
-
+
+
+
+
Header
+
+
+ Start Side Sidenav
+
+
+
+
+
+
+ Mode: {{start.mode}}
+
+
+ Filler Content
+
+
+
+ End Side Sidenav
+
+
+ Filler Content
+
+
+
+ Header
+
+
+
+
+
+
+
Sidenav
+
+
+ Fixed mode
+ Sidenav covers header/footer
+
+
+
+
Header / Footer
+ Show header
+ Show footer
+
+
+
Filler Content
+
+ Footer
+
+
+
Footer
+
diff --git a/src/demo-app/sidenav/sidenav-demo.scss b/src/demo-app/sidenav/sidenav-demo.scss
index a04b3bb58124..b9b359f85460 100644
--- a/src/demo-app/sidenav/sidenav-demo.scss
+++ b/src/demo-app/sidenav/sidenav-demo.scss
@@ -1,11 +1,25 @@
-.demo-sidenav-container {
- border: 3px solid black;
+.demo-sidenav-area {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ flex-direction: column;
- .mat-sidenav-focus-trap > .cdk-focus-trap-content {
- padding: 10px;
+ .mat-toolbar {
+ flex: 0;
+ }
+
+ .mat-sidenav-container {
+ flex: 1;
}
-}
-.demo-sidenav-content {
- padding: 15px;
+ .demo-sidenav-content {
+ padding: 32px;
+ }
+
+ .demo-filler-content {
+ padding: 60px;
+ }
}
diff --git a/src/demo-app/sidenav/sidenav-demo.ts b/src/demo-app/sidenav/sidenav-demo.ts
index 60be081c2a85..37a8873aad82 100644
--- a/src/demo-app/sidenav/sidenav-demo.ts
+++ b/src/demo-app/sidenav/sidenav-demo.ts
@@ -7,8 +7,16 @@ import {Component, ViewEncapsulation} from '@angular/core';
templateUrl: 'sidenav-demo.html',
styleUrls: ['sidenav-demo.css'],
encapsulation: ViewEncapsulation.None,
- preserveWhitespaces: false,
})
export class SidenavDemo {
- invert = false;
+ isLaunched = false;
+ fillerContent = Array(30);
+ fixed = false;
+ coverHeader = false;
+ showHeader = false;
+ showFooter = false;
+ modeIndex = 0;
+ get mode() { return ['side', 'over', 'push'][this.modeIndex]; }
+ get fixedTop() { return this.fixed && this.showHeader && !this.coverHeader ? 64 : 0; }
+ get fixedBottom() { return this.fixed && this.showFooter && !this.coverHeader ? 64 : 0; }
}
diff --git a/src/lib/sidenav/drawer-container.html b/src/lib/sidenav/drawer-container.html
index f305c0545a87..a7f8457f08ca 100644
--- a/src/lib/sidenav/drawer-container.html
+++ b/src/lib/sidenav/drawer-container.html
@@ -1,8 +1,10 @@
-
+
-
+
+
+
-
+
diff --git a/src/lib/sidenav/drawer-transitions.scss b/src/lib/sidenav/drawer-transitions.scss
index f14d83784f24..7be6e763b610 100644
--- a/src/lib/sidenav/drawer-transitions.scss
+++ b/src/lib/sidenav/drawer-transitions.scss
@@ -6,7 +6,7 @@
transition: {
duration: $swift-ease-out-duration;
timing-function: $swift-ease-out-timing-function;
- property: transform, margin-left, margin-right;
+ property: margin-left, margin-right;
}
}
diff --git a/src/lib/sidenav/drawer.html b/src/lib/sidenav/drawer.html
deleted file mode 100644
index 6dbc74306383..000000000000
--- a/src/lib/sidenav/drawer.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/lib/sidenav/drawer.scss b/src/lib/sidenav/drawer.scss
index 9d46906c21fd..d3efaa66d351 100644
--- a/src/lib/sidenav/drawer.scss
+++ b/src/lib/sidenav/drawer.scss
@@ -3,15 +3,21 @@
@import '../core/style/layout-common';
@import '../../cdk/a11y/a11y';
+$mat-drawer-content-z-index: 1;
+$mat-drawer-side-drawer-z-index: 2;
+$mat-drawer-backdrop-z-index: 3;
+$mat-drawer-over-drawer-z-index: 4;
+
// stylelint-disable max-line-length
// Mixin that creates a new stacking context.
// see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
// stylelint-enable
-@mixin mat-drawer-stacking-context() {
+@mixin mat-drawer-stacking-context($z-index:1) {
position: relative;
- // Use a transform to create a new stacking context.
- transform: translate3d(0, 0, 0);
+ // Use a z-index to create a new stacking context. (We can't use transform because it breaks fixed
+ // positioning inside of the transformed element).
+ z-index: $z-index;
}
.mat-drawer-container {
@@ -47,7 +53,7 @@
// Because of the new stacking context, the z-index stack is new and we can use our own
// numbers.
- z-index: 2;
+ z-index: $mat-drawer-backdrop-z-index;
// We use 'visibility: hidden | visible' because 'display: none' will not animate any
// transitions, while visibility will interpolate transitions properly.
@@ -65,7 +71,7 @@
}
.mat-drawer-content {
- @include mat-drawer-stacking-context();
+ @include mat-drawer-stacking-context($mat-drawer-content-z-index);
display: block;
height: 100%;
@@ -73,7 +79,7 @@
}
.mat-drawer {
- @include mat-drawer-stacking-context();
+ @include mat-drawer-stacking-context($mat-drawer-over-drawer-z-index);
display: block;
position: absolute;
@@ -83,12 +89,11 @@
min-width: 5vw;
outline: 0;
box-sizing: border-box;
- height: 100%;
overflow-y: auto; // TODO(kara): revisit scrolling behavior for drawers
transform: translate3d(-100%, 0, 0);
&.mat-drawer-side {
- z-index: 1;
+ z-index: $mat-drawer-side-drawer-z-index;
}
&.mat-drawer-end {
@@ -114,3 +119,7 @@
}
}
}
+
+.mat-sidenav-fixed {
+ position: fixed;
+}
diff --git a/src/lib/sidenav/drawer.spec.ts b/src/lib/sidenav/drawer.spec.ts
index 6d0a5171a06b..d858a6a2d8a1 100644
--- a/src/lib/sidenav/drawer.spec.ts
+++ b/src/lib/sidenav/drawer.spec.ts
@@ -76,6 +76,8 @@ describe('MdDrawer', () => {
it('should emit the backdropClick event when the backdrop is clicked', fakeAsync(() => {
let fixture = TestBed.createComponent(BasicTestApp);
+ fixture.detectChanges();
+
let testComponent: BasicTestApp = fixture.debugElement.componentInstance;
let openButtonElement = fixture.debugElement.query(By.css('.open')).nativeElement;
diff --git a/src/lib/sidenav/drawer.ts b/src/lib/sidenav/drawer.ts
index ef241cacd3fa..3d306303fac0 100644
--- a/src/lib/sidenav/drawer.ts
+++ b/src/lib/sidenav/drawer.ts
@@ -16,9 +16,11 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
+ ContentChild,
ContentChildren,
ElementRef,
EventEmitter,
+ forwardRef,
Inject,
Input,
NgZone,
@@ -34,6 +36,7 @@ import {merge} from 'rxjs/observable/merge';
import {first} from 'rxjs/operator/first';
import {startWith} from 'rxjs/operator/startWith';
import {takeUntil} from 'rxjs/operator/takeUntil';
+import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription';
@@ -51,6 +54,42 @@ export class MdDrawerToggleResult {
constructor(public type: 'open' | 'close', public animationFinished: boolean) {}
}
+
+@Component({
+ moduleId: module.id,
+ selector: 'md-drawer-content, mat-drawer-content',
+ template: '',
+ host: {
+ 'class': 'mat-drawer-content',
+ '[style.marginLeft.px]': '_margins.left',
+ '[style.marginRight.px]': '_margins.right',
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ encapsulation: ViewEncapsulation.None,
+ preserveWhitespaces: false,
+})
+export class MdDrawerContent implements AfterContentInit {
+ /**
+ * Margins to be applied to the content. These are used to push / shrink the drawer content when a
+ * drawer is open. We use margin rather than transform even for push mode because transform breaks
+ * fixed position elements inside of the transformed element.
+ */
+ _margins: {left: number, right: number} = {left: 0, right: 0};
+
+ constructor(
+ private _changeDetectorRef: ChangeDetectorRef,
+ @Inject(forwardRef(() => MdDrawerContainer)) private _container: MdDrawerContainer) {
+ }
+
+ ngAfterContentInit() {
+ this._container._contentMargins.subscribe(margins => {
+ this._margins = margins;
+ this._changeDetectorRef.markForCheck();
+ });
+ }
+}
+
+
/**
* component.
*
@@ -61,7 +100,7 @@ export class MdDrawerToggleResult {
@Component({
moduleId: module.id,
selector: 'md-drawer, mat-drawer',
- templateUrl: 'drawer.html',
+ template: '',
animations: [
trigger('transform', [
state('open, open-instant', style({
@@ -121,7 +160,13 @@ export class MdDrawer implements AfterContentInit, OnDestroy {
set align(value) { this.position = value; }
/** Mode of the drawer; one of 'over', 'push' or 'side'. */
- @Input() mode: 'over' | 'push' | 'side' = 'over';
+ @Input()
+ get mode() { return this._mode; }
+ set mode(value) {
+ this._mode = value;
+ this._modeChanged.next();
+ }
+ private _mode: 'over' | 'push' | 'side' = 'over';
/** Whether the drawer can be closed with the escape key or by clicking on the backdrop. */
@Input()
@@ -160,6 +205,12 @@ export class MdDrawer implements AfterContentInit, OnDestroy {
/** @deprecated */
@Output('align-changed') onAlignChanged = new EventEmitter();
+ /**
+ * An observable that emits when the drawer mode changes. This is used by the drawer container to
+ * to know when to when the mode changes so it can adapt the margins on the content.
+ */
+ _modeChanged = new Subject();
+
get isFocusTrapEnabled() {
// The focus trap is only enabled when the drawer is open in any mode other than side.
return this.opened && this.mode !== 'side';
@@ -298,6 +349,7 @@ export class MdDrawer implements AfterContentInit, OnDestroy {
}
}
+
/**
* component.
*
@@ -322,6 +374,8 @@ export class MdDrawer implements AfterContentInit, OnDestroy {
export class MdDrawerContainer implements AfterContentInit, OnDestroy {
@ContentChildren(MdDrawer) _drawers: QueryList;
+ @ContentChild(MdDrawerContent) _content: MdDrawerContent;
+
/** The drawer child with the `start` position. */
get start() { return this._start; }
@@ -347,8 +401,7 @@ export class MdDrawerContainer implements AfterContentInit, OnDestroy {
/** Subscription to the Directionality change EventEmitter. */
private _dirChangeSubscription = Subscription.EMPTY;
- /** Inline styles to be applied to the container. */
- _styles: { marginLeft: string; marginRight: string; transform: string; };
+ _contentMargins = new Subject<{left: number, right: number}>();
constructor(@Optional() private _dir: Directionality, private _element: ElementRef,
private _renderer: Renderer2, private _ngZone: NgZone,
@@ -366,6 +419,7 @@ export class MdDrawerContainer implements AfterContentInit, OnDestroy {
this._drawers.forEach((drawer: MdDrawer) => {
this._watchDrawerToggle(drawer);
this._watchDrawerPosition(drawer);
+ this._watchDrawerMode(drawer);
});
});
}
@@ -394,7 +448,7 @@ export class MdDrawerContainer implements AfterContentInit, OnDestroy {
// Set the transition class on the container so that the animations occur. This should not
// be set initially because animations should only be triggered via a change in state.
this._renderer.addClass(this._element.nativeElement, 'mat-drawer-transition');
- this._updateStyles();
+ this._updateContentMargins();
this._changeDetectorRef.markForCheck();
});
@@ -421,6 +475,16 @@ export class MdDrawerContainer implements AfterContentInit, OnDestroy {
});
}
+ /** Subscribes to changes in drawer mode so we can run change detection. */
+ private _watchDrawerMode(drawer: MdDrawer): void {
+ if (drawer) {
+ takeUntil.call(drawer._modeChanged, this._drawers.changes).subscribe(() => {
+ this._updateContentMargins();
+ this._changeDetectorRef.markForCheck();
+ });
+ }
+ }
+
/** Toggles the 'mat-drawer-opened' class on the main 'md-drawer-container' element. */
private _setContainerClass(isAdd: boolean): void {
if (isAdd) {
@@ -483,29 +547,40 @@ export class MdDrawerContainer implements AfterContentInit, OnDestroy {
}
/**
- * Return the width of the drawer, if it's in the proper mode and opened.
- * This may relayout the view, so do not call this often.
- * @param drawer
- * @param mode
+ * Recalculates and updates the inline styles for the content. Note that this should be used
+ * sparingly, because it causes a reflow.
*/
- private _getDrawerEffectiveWidth(drawer: MdDrawer, mode: string): number {
- return (this._isDrawerOpen(drawer) && drawer.mode == mode) ? drawer._width : 0;
- }
+ private _updateContentMargins() {
+ // 1. For drawers in `over` mode, they don't affect the content.
+ // 2. For drawers in `side` mode they should shrink the content. We do this by adding to the
+ // left margin (for left drawer) or right margin (for right the drawer).
+ // 3. For drawers in `push` mode the should shift the content without resizing it. We do this by
+ // adding to the left or right margin and simultaneously subtracting the same amount of
+ // margin from the other side.
+
+ let left = 0;
+ let right = 0;
+
+ if (this._left && this._left.opened) {
+ if (this._left.mode == 'side') {
+ left += this._left._width;
+ } else if (this._left.mode == 'push') {
+ let width = this._left._width;
+ left += width;
+ right -= width;
+ }
+ }
- /**
- * Recalculates and updates the inline styles. Note that this
- * should be used sparingly, because it causes a reflow.
- */
- private _updateStyles() {
- const marginLeft = this._left ? this._getDrawerEffectiveWidth(this._left, 'side') : 0;
- const marginRight = this._right ? this._getDrawerEffectiveWidth(this._right, 'side') : 0;
- const leftWidth = this._left ? this._getDrawerEffectiveWidth(this._left, 'push') : 0;
- const rightWidth = this._right ? this._getDrawerEffectiveWidth(this._right, 'push') : 0;
-
- this._styles = {
- marginLeft: `${marginLeft}px`,
- marginRight: `${marginRight}px`,
- transform: `translate3d(${leftWidth - rightWidth}px, 0, 0)`
- };
+ if (this._right && this._right.opened) {
+ if (this._right.mode == 'side') {
+ right += this._right._width;
+ } else if (this._right.mode == 'push') {
+ let width = this._right._width;
+ right += width;
+ left -= width;
+ }
+ }
+
+ this._contentMargins.next({left, right});
}
}
diff --git a/src/lib/sidenav/sidenav-container.html b/src/lib/sidenav/sidenav-container.html
new file mode 100644
index 000000000000..7068bbdf4382
--- /dev/null
+++ b/src/lib/sidenav/sidenav-container.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/sidenav/sidenav-module.ts b/src/lib/sidenav/sidenav-module.ts
index 16f14d9d98fc..577f9205826c 100644
--- a/src/lib/sidenav/sidenav-module.ts
+++ b/src/lib/sidenav/sidenav-module.ts
@@ -6,18 +6,33 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {NgModule} from '@angular/core';
-import {CommonModule} from '@angular/common';
import {A11yModule} from '@angular/cdk/a11y';
import {OverlayModule} from '@angular/cdk/overlay';
+import {CommonModule} from '@angular/common';
+import {NgModule} from '@angular/core';
import {MdCommonModule} from '@angular/material/core';
-import {MdDrawer, MdDrawerContainer} from './drawer';
-import {MdSidenav, MdSidenavContainer} from './sidenav';
+import {MdDrawer, MdDrawerContainer, MdDrawerContent} from './drawer';
+import {MdSidenav, MdSidenavContainer, MdSidenavContent} from './sidenav';
@NgModule({
imports: [CommonModule, MdCommonModule, A11yModule, OverlayModule],
- exports: [MdDrawerContainer, MdDrawer, MdSidenavContainer, MdSidenav, MdCommonModule],
- declarations: [MdDrawerContainer, MdDrawer, MdSidenavContainer, MdSidenav],
+ exports: [
+ MdCommonModule,
+ MdDrawer,
+ MdDrawerContainer,
+ MdDrawerContent,
+ MdSidenav,
+ MdSidenavContainer,
+ MdSidenavContent,
+ ],
+ declarations: [
+ MdDrawer,
+ MdDrawerContainer,
+ MdDrawerContent,
+ MdSidenav,
+ MdSidenavContainer,
+ MdSidenavContent,
+ ],
})
export class MdSidenavModule {}
diff --git a/src/lib/sidenav/sidenav.spec.ts b/src/lib/sidenav/sidenav.spec.ts
new file mode 100644
index 000000000000..0de4571a2638
--- /dev/null
+++ b/src/lib/sidenav/sidenav.spec.ts
@@ -0,0 +1,67 @@
+import {Component} from '@angular/core';
+import {async, TestBed, ComponentFixture} from '@angular/core/testing';
+import {MdSidenav, MdSidenavModule} from './index';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {By} from '@angular/platform-browser';
+
+
+describe('MdSidenav', () => {
+ let fixture: ComponentFixture;
+ let sidenavEl: HTMLElement;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [MdSidenavModule, NoopAnimationsModule],
+ declarations: [SidenavWithFixedPosition],
+ });
+
+ TestBed.compileComponents();
+
+ fixture = TestBed.createComponent(SidenavWithFixedPosition);
+ fixture.detectChanges();
+
+ sidenavEl = fixture.debugElement.query(By.directive(MdSidenav)).nativeElement;
+ }));
+
+ it('should be fixed position when in fixed mode', () => {
+ expect(sidenavEl.classList).toContain('mat-sidenav-fixed');
+
+ fixture.componentInstance.fixed = false;
+ fixture.detectChanges();
+
+ expect(sidenavEl.classList).not.toContain('mat-sidenav-fixed');
+ });
+
+ it('should set fixed bottom and top when in fixed mode', () => {
+ expect(sidenavEl.style.top).toBe('20px');
+ expect(sidenavEl.style.bottom).toBe('30px');
+
+ fixture.componentInstance.fixed = false;
+ fixture.detectChanges();
+
+ expect(sidenavEl.style.top).toBeFalsy();
+ expect(sidenavEl.style.bottom).toBeFalsy();
+ });
+});
+
+
+@Component({
+ template: `
+
+
+ Drawer.
+
+
+ Some content.
+
+ `,
+})
+class SidenavWithFixedPosition {
+ fixed = true;
+ fixedTop = 20;
+ fixedBottom = 30;
+}
diff --git a/src/lib/sidenav/sidenav.ts b/src/lib/sidenav/sidenav.ts
index 12e8b925bcfa..aff0a5b25137 100644
--- a/src/lib/sidenav/sidenav.ts
+++ b/src/lib/sidenav/sidenav.ts
@@ -7,19 +7,42 @@
*/
import {
- ChangeDetectionStrategy,
- Component,
- ContentChildren,
+ ChangeDetectionStrategy, ChangeDetectorRef,
+ Component, ContentChild,
+ ContentChildren, forwardRef, Inject, Input,
ViewEncapsulation
} from '@angular/core';
-import {MdDrawer, MdDrawerContainer} from './drawer';
+import {MdDrawer, MdDrawerContainer, MdDrawerContent} from './drawer';
import {animate, state, style, transition, trigger} from '@angular/animations';
+import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'md-sidenav-content, mat-sidenav-content',
+ template: '',
+ host: {
+ 'class': 'mat-drawer-content mat-sidenav-content',
+ '[style.marginLeft.px]': '_margins.left',
+ '[style.marginRight.px]': '_margins.right',
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ encapsulation: ViewEncapsulation.None,
+ preserveWhitespaces: false,
+})
+export class MdSidenavContent extends MdDrawerContent {
+ constructor(
+ changeDetectorRef: ChangeDetectorRef,
+ @Inject(forwardRef(() => MdSidenavContainer)) container: MdSidenavContainer) {
+ super(changeDetectorRef, container);
+ }
+}
@Component({
moduleId: module.id,
selector: 'md-sidenav, mat-sidenav',
- templateUrl: 'drawer.html',
+ template: '',
animations: [
trigger('transform', [
state('open, open-instant', style({
@@ -36,6 +59,7 @@ import {animate, state, style, transition, trigger} from '@angular/animations';
],
host: {
'class': 'mat-drawer mat-sidenav',
+ 'tabIndex': '-1',
'[@transform]': '_animationState',
'(@transform.start)': '_onAnimationStart()',
'(@transform.done)': '_onAnimationEnd($event)',
@@ -46,19 +70,45 @@ import {animate, state, style, transition, trigger} from '@angular/animations';
'[class.mat-drawer-over]': 'mode === "over"',
'[class.mat-drawer-push]': 'mode === "push"',
'[class.mat-drawer-side]': 'mode === "side"',
- 'tabIndex': '-1',
+ '[class.mat-sidenav-fixed]': 'fixedInViewport',
+ '[style.top.px]': 'fixedInViewport ? fixedTopGap : null',
+ '[style.bottom.px]': 'fixedInViewport ? fixedBottomGap : null',
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
})
-export class MdSidenav extends MdDrawer {}
+export class MdSidenav extends MdDrawer {
+ /** Whether the sidenav is fixed in the viewport. */
+ @Input()
+ get fixedInViewport() { return this._fixedInViewport; }
+ set fixedInViewport(value) { this._fixedInViewport = coerceBooleanProperty(value); }
+ private _fixedInViewport = false;
+
+ /**
+ * The gap between the top of the sidenav and the top of the viewport when the sidenav is in fixed
+ * mode.
+ */
+ @Input()
+ get fixedTopGap() { return this._fixedTopGap; }
+ set fixedTopGap(value) { this._fixedTopGap = coerceNumberProperty(value); }
+ private _fixedTopGap = 0;
+
+ /**
+ * The gap between the bottom of the sidenav and the bottom of the viewport when the sidenav is in
+ * fixed mode.
+ */
+ @Input()
+ get fixedBottomGap() { return this._fixedBottomGap; }
+ set fixedBottomGap(value) { this._fixedBottomGap = coerceNumberProperty(value); }
+ private _fixedBottomGap = 0;
+}
@Component({
moduleId: module.id,
selector: 'md-sidenav-container, mat-sidenav-container',
- templateUrl: 'drawer-container.html',
+ templateUrl: 'sidenav-container.html',
styleUrls: [
'drawer.css',
'drawer-transitions.css',
@@ -72,4 +122,6 @@ export class MdSidenav extends MdDrawer {}
})
export class MdSidenavContainer extends MdDrawerContainer {
@ContentChildren(MdSidenav) _drawers;
+
+ @ContentChild(MdSidenavContent) _content;
}