diff --git a/src/components/dialog/dialog.spec.ts b/src/components/dialog/dialog.spec.ts index 56723c2b15ce..e0898379d0fc 100644 --- a/src/components/dialog/dialog.spec.ts +++ b/src/components/dialog/dialog.spec.ts @@ -13,7 +13,8 @@ import { ChangeDetectorRef, } from '@angular/core'; import {MdDialog} from './dialog'; -import {OVERLAY_PROVIDERS, OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay'; +import {OVERLAY_PROVIDERS} from '@angular2-material/core/overlay/overlay'; +import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container'; import {MdDialogConfig} from './dialog-config'; import {MdDialogRef} from './dialog-ref'; @@ -31,10 +32,14 @@ describe('MdDialog', () => { addProviders([ OVERLAY_PROVIDERS, MdDialog, - {provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => { - overlayContainerElement = document.createElement('div'); - return overlayContainerElement; - }} + {provide: OverlayContainer, useFactory: () => { + return { + getContainerElement: () => { + overlayContainerElement = document.createElement('div'); + return overlayContainerElement; + } + }; + }}, ]); }); diff --git a/src/components/menu/menu-trigger.ts b/src/components/menu/menu-trigger.ts index e0972f412901..e4db939cb310 100644 --- a/src/components/menu/menu-trigger.ts +++ b/src/components/menu/menu-trigger.ts @@ -71,7 +71,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { } destroyMenu(): void { - this._overlayRef.dispose(); + if (this._overlayRef) { this._overlayRef.dispose(); } } // set state rather than toggle to support triggers sharing a menu diff --git a/src/components/tooltip/tooltip.spec.ts b/src/components/tooltip/tooltip.spec.ts index 9a06708ae188..d6cb38d1c0c5 100644 --- a/src/components/tooltip/tooltip.spec.ts +++ b/src/components/tooltip/tooltip.spec.ts @@ -8,11 +8,12 @@ import { beforeEachProviders, } from '@angular/core/testing'; import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing'; -import {Component, provide, DebugElement} from '@angular/core'; +import {Component, DebugElement} from '@angular/core'; import {By} from '@angular/platform-browser'; import {MD_TOOLTIP_DIRECTIVES, TooltipPosition, MdTooltip} from '@angular2-material/tooltip/tooltip'; -import {OVERLAY_PROVIDERS, OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay'; +import {OVERLAY_PROVIDERS} from '@angular2-material/core/overlay/overlay'; +import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container'; describe('MdTooltip', () => { let builder: TestComponentBuilder; @@ -20,12 +21,14 @@ describe('MdTooltip', () => { beforeEachProviders(() => [ OVERLAY_PROVIDERS, - provide(OVERLAY_CONTAINER_TOKEN, { - useFactory: () => { - overlayContainerElement = document.createElement('div'); - return overlayContainerElement; - } - }) + {provide: OverlayContainer, useFactory: () => { + return { + getContainerElement: () => { + overlayContainerElement = document.createElement('div'); + return overlayContainerElement; + } + }; + }}, ]); beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { diff --git a/src/core/core.ts b/src/core/core.ts index cffa1091703f..956b1df7810b 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -17,7 +17,8 @@ export { export {DomPortalHost} from './portal/dom-portal-host'; // Overlay -export {Overlay, OVERLAY_CONTAINER_TOKEN, OVERLAY_PROVIDERS} from './overlay/overlay'; +export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay'; +export {OverlayContainer} from './overlay/overlay-container'; export {OverlayRef} from './overlay/overlay-ref'; export {OverlayState} from './overlay/overlay-state'; export { diff --git a/src/core/overlay/overlay-container.ts b/src/core/overlay/overlay-container.ts index e5e49599f5b2..156812a43c96 100644 --- a/src/core/overlay/overlay-container.ts +++ b/src/core/overlay/overlay-container.ts @@ -1,12 +1,29 @@ - - /** - * Create the overlay container element, which is simply a div - * with the 'md-overlay-container' class on the document body. + * The OverlayContainer is the container in which all overlays will load. + * It should be provided in the root component to ensure it is properly shared. */ -export function createOverlayContainer(): Element { - let container = document.createElement('div'); - container.classList.add('md-overlay-container'); - document.body.appendChild(container); - return container; +export class OverlayContainer { + private _containerElement: HTMLElement; + + /** + * This method returns the overlay container element. It will lazily + * create the element the first time it is called to facilitate using + * the container in non-browser environments. + * @returns {HTMLElement} the container element + */ + getContainerElement(): HTMLElement { + if (!this._containerElement) { this._createContainer(); } + return this._containerElement; + } + + /** + * Create the overlay container element, which is simply a div + * with the 'md-overlay-container' class on the document body. + */ + private _createContainer(): void { + let container = document.createElement('div'); + container.classList.add('md-overlay-container'); + document.body.appendChild(container); + this._containerElement = container; + } } diff --git a/src/core/overlay/overlay-directives.spec.ts b/src/core/overlay/overlay-directives.spec.ts index f146609297f1..4ca27565f1d5 100644 --- a/src/core/overlay/overlay-directives.spec.ts +++ b/src/core/overlay/overlay-directives.spec.ts @@ -8,12 +8,12 @@ import { import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing'; import {Component, ViewChild} from '@angular/core'; import {ConnectedOverlayDirective, OverlayOrigin} from './overlay-directives'; -import {OVERLAY_CONTAINER_TOKEN, Overlay} from './overlay'; +import {Overlay} from './overlay'; +import {OverlayContainer} from './overlay-container'; import {ViewportRuler} from './position/viewport-ruler'; import {OverlayPositionBuilder} from './position/overlay-position-builder'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; - describe('Overlay directives', () => { let builder: TestComponentBuilder; let overlayContainerElement: HTMLElement; @@ -24,9 +24,13 @@ describe('Overlay directives', () => { Overlay, OverlayPositionBuilder, ViewportRuler, - {provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => { - overlayContainerElement = document.createElement('div'); - return overlayContainerElement; + {provide: OverlayContainer, useFactory: () => { + return { + getContainerElement: () => { + overlayContainerElement = document.createElement('div'); + return overlayContainerElement; + } + }; }}, ]); }); diff --git a/src/core/overlay/overlay.scss b/src/core/overlay/overlay.scss index 8dbb2408f378..d04ea0f48f72 100644 --- a/src/core/overlay/overlay.scss +++ b/src/core/overlay/overlay.scss @@ -1,5 +1,7 @@ // TODO(jelbourn): change from the `md` prefix to something else for everything in the toolkit. +@import 'variables'; + /** The overlay-container is an invisible element which contains all individual overlays. */ .md-overlay-container { position: absolute; @@ -19,5 +21,5 @@ position: absolute; pointer-events: auto; box-sizing: border-box; - z-index: 1; + z-index: $z-index-overlay; } diff --git a/src/core/overlay/overlay.spec.ts b/src/core/overlay/overlay.spec.ts index c3494570cf2b..ec04528aae20 100644 --- a/src/core/overlay/overlay.spec.ts +++ b/src/core/overlay/overlay.spec.ts @@ -12,7 +12,8 @@ import { } from '@angular/core'; import {TemplatePortalDirective} from '../portal/portal-directives'; import {TemplatePortal, ComponentPortal} from '../portal/portal'; -import {Overlay, OVERLAY_CONTAINER_TOKEN} from './overlay'; +import {Overlay} from './overlay'; +import {OverlayContainer} from './overlay-container'; import {OverlayRef} from './overlay-ref'; import {OverlayState} from './overlay-state'; import {PositionStrategy} from './position/position-strategy'; @@ -32,9 +33,14 @@ describe('Overlay', () => { Overlay, OverlayPositionBuilder, ViewportRuler, - {provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => { - overlayContainerElement = document.createElement('div'); - return overlayContainerElement; + {provide: OverlayContainer, useFactory: () => { + return { + getContainerElement: () => { + if (overlayContainerElement) { return overlayContainerElement; } + overlayContainerElement = document.createElement('div'); + return overlayContainerElement; + } + }; }} ]); }); diff --git a/src/core/overlay/overlay.ts b/src/core/overlay/overlay.ts index 0471f5084607..934806eb83a0 100644 --- a/src/core/overlay/overlay.ts +++ b/src/core/overlay/overlay.ts @@ -1,7 +1,5 @@ import { ComponentResolver, - OpaqueToken, - Inject, Injectable, } from '@angular/core'; import {OverlayState} from './overlay-state'; @@ -10,10 +8,7 @@ import {OverlayRef} from './overlay-ref'; import {OverlayPositionBuilder} from './position/overlay-position-builder'; import {ViewportRuler} from './position/viewport-ruler'; - - -/** Token used to inject the DOM element that serves as the overlay container. */ -export const OVERLAY_CONTAINER_TOKEN = new OpaqueToken('overlayContainer'); +import {OverlayContainer} from './overlay-container'; /** Next overlay unique ID. */ let nextUniqueId = 0; @@ -32,18 +27,9 @@ let defaultState = new OverlayState(); */ @Injectable() export class Overlay { - private _overlayContainerElement: HTMLElement; - - constructor( - @Inject(OVERLAY_CONTAINER_TOKEN) overlayContainerElement: any, - private _componentResolver: ComponentResolver, - private _positionBuilder: OverlayPositionBuilder) { - - // We inject the container as `any` because the constructor signature cannot reference - // browser globals (HTMLElement) on non-browser environments, since having a class decorator - // causes TypeScript to preserve the constructor signature types. - this._overlayContainerElement = overlayContainerElement; - } + constructor(private _overlayContainer: OverlayContainer, + private _componentResolver: ComponentResolver, + private _positionBuilder: OverlayPositionBuilder) {} /** * Creates an overlay. @@ -71,7 +57,7 @@ export class Overlay { pane.id = `md-overlay-${nextUniqueId++}`; pane.classList.add('md-overlay-pane'); - this._overlayContainerElement.appendChild(pane); + this._overlayContainer.getContainerElement().appendChild(pane); return Promise.resolve(pane); } diff --git a/src/core/style/_variables.scss b/src/core/style/_variables.scss index 55877597d460..d69d5ad651cc 100644 --- a/src/core/style/_variables.scss +++ b/src/core/style/_variables.scss @@ -8,9 +8,11 @@ $md-font-family: Roboto, 'Helvetica Neue', sans-serif !default; // Media queries $md-xsmall: 'max-width: 600px'; +// TODO: Revisit all z-indices before beta // z-index master list $z-index-fab: 20 !default; $z-index-drawer: 100 !default; +$z-index-overlay: 1000 !default; // Global constants $pi: 3.14159265; diff --git a/src/demo-app/main.ts b/src/demo-app/main.ts index b43d71a7119a..e2037c0c6e83 100644 --- a/src/demo-app/main.ts +++ b/src/demo-app/main.ts @@ -2,13 +2,10 @@ import {bootstrap} from '@angular/platform-browser-dynamic'; import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {HTTP_PROVIDERS} from '@angular/http'; import {disableDeprecatedForms, provideForms} from '@angular/forms'; - -import {OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay'; import {MdLiveAnnouncer} from '@angular2-material/core/a11y/live-announcer'; -import {createOverlayContainer} from '@angular2-material/core/overlay/overlay-container'; import {MdGestureConfig} from '@angular2-material/core/gestures/MdGestureConfig'; import {MdIconRegistry} from '@angular2-material/icon/icon-registry'; - +import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container'; import {DemoApp} from './demo-app/demo-app'; import {DEMO_APP_ROUTE_PROVIDER} from './demo-app/routes'; @@ -17,8 +14,8 @@ bootstrap(DemoApp, [ disableDeprecatedForms(), provideForms(), MdLiveAnnouncer, - {provide: OVERLAY_CONTAINER_TOKEN, useValue: createOverlayContainer()}, HTTP_PROVIDERS, + OverlayContainer, MdIconRegistry, {provide: HAMMER_GESTURE_CONFIG, useClass: MdGestureConfig}, ]); diff --git a/src/e2e-app/main.ts b/src/e2e-app/main.ts index a86dbf408839..c0511c96fa61 100644 --- a/src/e2e-app/main.ts +++ b/src/e2e-app/main.ts @@ -3,13 +3,10 @@ import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {HTTP_PROVIDERS} from '@angular/http'; import {Renderer} from '@angular/core'; import {disableDeprecatedForms, provideForms} from '@angular/forms'; - -import {OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay'; import {MdLiveAnnouncer} from '@angular2-material/core/a11y/live-announcer'; -import {createOverlayContainer} from '@angular2-material/core/overlay/overlay-container'; import {MdGestureConfig} from '@angular2-material/core/gestures/MdGestureConfig'; import {MdIconRegistry} from '@angular2-material/icon/icon-registry'; - +import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container'; import {E2EApp} from './e2e-app/e2e-app'; import {E2E_APP_ROUTE_PROVIDER} from './e2e-app/routes'; @@ -18,7 +15,7 @@ bootstrap(E2EApp, [ disableDeprecatedForms(), provideForms(), MdLiveAnnouncer, - {provide: OVERLAY_CONTAINER_TOKEN, useValue: createOverlayContainer()}, + OverlayContainer, HTTP_PROVIDERS, MdIconRegistry, Renderer,