mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(overlay): add fullscreen-enabled overlay class (#1949)
* feat(overlay): add fullscreen overlay class * fix lint error * move fs container to a new file and rename it * add e2e tests * fix tests * rebased and fix comments * fix typings * fix e2e tests * address comments * fix tests
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import {browser, by, element, Key, ProtractorBy} from 'protractor';
|
||||
|
||||
describe('fullscreen', () => {
|
||||
beforeEach(() => browser.get('/fullscreen'));
|
||||
|
||||
let overlayInBody = () =>
|
||||
browser.isElementPresent(by.css('body > .cdk-overlay-container') as ProtractorBy);
|
||||
let overlayInFullscreen = () =>
|
||||
browser.isElementPresent(by.css('#fullscreenpane > .cdk-overlay-container') as ProtractorBy);
|
||||
|
||||
it('should open a dialog inside a fullscreen element and move it to the document body', () => {
|
||||
element(by.id('fullscreen')).click();
|
||||
element(by.id('dialog')).click();
|
||||
|
||||
overlayInFullscreen().then((isPresent: boolean) => {
|
||||
expect(isPresent).toBe(true);
|
||||
element(by.id('exitfullscreenindialog')).click();
|
||||
overlayInBody().then((isPresent: boolean) => {
|
||||
expect(isPresent).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should open a dialog inside the document body and move it to a fullscreen element', () => {
|
||||
element(by.id('dialog')).click();
|
||||
overlayInBody().then((isPresent: boolean) => {
|
||||
expect(isPresent).toBe(true);
|
||||
element(by.id('fullscreenindialog')).click();
|
||||
overlayInFullscreen().then((isPresent: boolean) => {
|
||||
expect(isPresent).toBe(true);
|
||||
element(by.id('exitfullscreenindialog')).click();
|
||||
overlayInBody().then((isPresent: boolean) => {
|
||||
expect(isPresent).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,8 @@ import {HttpModule} from '@angular/http';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {DemoApp, Home} from './demo-app/demo-app';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {MaterialModule} from '@angular/material';
|
||||
import {MaterialModule, OverlayContainer,
|
||||
FullscreenOverlayContainer} from '@angular/material';
|
||||
import {DEMO_APP_ROUTES} from './demo-app/routes';
|
||||
import {ProgressBarDemo} from './progress-bar/progress-bar-demo';
|
||||
import {JazzDialog, ContentElementDialog, DialogDemo} from './dialog/dialog-demo';
|
||||
@@ -94,6 +95,9 @@ import {InputContainerDemo} from './input/input-container-demo';
|
||||
FoggyTabContent,
|
||||
PlatformDemo
|
||||
],
|
||||
providers: [
|
||||
{provide: OverlayContainer, useClass: FullscreenOverlayContainer}
|
||||
],
|
||||
entryComponents: [
|
||||
DemoApp,
|
||||
JazzDialog,
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
</button>
|
||||
<div class="demo-toolbar">
|
||||
<h1>Angular Material 2 Demos</h1>
|
||||
<button md-button (click)="toggleFullscreen()" title="Toggle fullscreen">
|
||||
Fullscreen
|
||||
</button>
|
||||
<button md-button (click)="root.dir = (root.dir == 'rtl' ? 'ltr' : 'rtl')" title="Toggle between RTL and LTR">
|
||||
{{root.dir.toUpperCase()}}
|
||||
</button>
|
||||
|
||||
@@ -39,3 +39,9 @@ body {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// stretch to screen size in fullscreen mode
|
||||
.demo-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component, ViewEncapsulation} from '@angular/core';
|
||||
import {Component, ViewEncapsulation, ElementRef} from '@angular/core';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -52,4 +52,21 @@ export class DemoApp {
|
||||
{name: 'Tooltip', route: 'tooltip'},
|
||||
{name: 'Platform', route: 'platform'}
|
||||
];
|
||||
|
||||
constructor(private _element: ElementRef) {
|
||||
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
let elem = this._element.nativeElement.querySelector('.demo-content');
|
||||
if (elem.requestFullscreen) {
|
||||
elem.requestFullscreen();
|
||||
} else if (elem.webkitRequestFullScreen) {
|
||||
elem.webkitRequestFullScreen();
|
||||
} else if (elem.mozRequestFullScreen) {
|
||||
elem.mozRequestFullScreen();
|
||||
} else if (elem.msRequestFullScreen) {
|
||||
elem.msRequestFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ import {GridListE2E} from './grid-list/grid-list-e2e';
|
||||
import {ListE2E} from './list/list-e2e';
|
||||
import {ProgressBarE2E} from './progress-bar/progress-bar-e2e';
|
||||
import {ProgressSpinnerE2E} from './progress-spinner/progress-spinner-e2e';
|
||||
import {MaterialModule} from '@angular/material';
|
||||
import {FullscreenE2E, TestDialog as TestDialogFullScreen} from './fullscreen/fullscreen-e2e';
|
||||
import {MaterialModule, OverlayContainer, FullscreenOverlayContainer} from '@angular/material';
|
||||
import {E2E_APP_ROUTES} from './e2e-app/routes';
|
||||
import {SlideToggleE2E} from './slide-toggle/slide-toggle-e2e';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -39,12 +39,15 @@ import {SlideToggleE2E} from './slide-toggle/slide-toggle-e2e';
|
||||
ListE2E,
|
||||
ProgressBarE2E,
|
||||
ProgressSpinnerE2E,
|
||||
SlideToggleE2E
|
||||
SlideToggleE2E,
|
||||
FullscreenE2E,
|
||||
TestDialogFullScreen
|
||||
],
|
||||
bootstrap: [E2EApp],
|
||||
providers: [
|
||||
{provide: AnimationDriver, useValue: AnimationDriver.NOOP}
|
||||
{provide: AnimationDriver, useValue: AnimationDriver.NOOP},
|
||||
{provide: OverlayContainer, useClass: FullscreenOverlayContainer}
|
||||
],
|
||||
entryComponents: [TestDialog]
|
||||
entryComponents: [TestDialog, TestDialogFullScreen]
|
||||
})
|
||||
export class E2eAppModule { }
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<a md-list-item [routerLink]="['radio']">Radios</a>
|
||||
<a md-list-item [routerLink]="['slide-toggle']">Slide Toggle</a>
|
||||
<a md-list-item [routerLink]="['tabs']">Tabs</a>
|
||||
<a md-list-item [routerLink]="['fullscreen']">Fullscreen</a>
|
||||
</md-nav-list>
|
||||
|
||||
<main>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {ListE2E} from '../list/list-e2e';
|
||||
import {ProgressBarE2E} from '../progress-bar/progress-bar-e2e';
|
||||
import {ProgressSpinnerE2E} from '../progress-spinner/progress-spinner-e2e';
|
||||
import {SlideToggleE2E} from '../slide-toggle/slide-toggle-e2e';
|
||||
import {FullscreenE2E} from '../fullscreen/fullscreen-e2e';
|
||||
|
||||
export const E2E_APP_ROUTES: Routes = [
|
||||
{path: '', component: Home},
|
||||
@@ -26,5 +27,6 @@ export const E2E_APP_ROUTES: Routes = [
|
||||
{path: 'list', component: ListE2E},
|
||||
{path: 'progress-bar', component: ProgressBarE2E},
|
||||
{path: 'progress-spinner', component: ProgressSpinnerE2E},
|
||||
{path: 'slide-toggle', component: SlideToggleE2E}
|
||||
{path: 'slide-toggle', component: SlideToggleE2E},
|
||||
{path: 'fullscreen', component: FullscreenE2E}
|
||||
];
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<button id="fullscreen" (click)="toggleFullScreen()">FULLSCREEN</button>
|
||||
<div id="fullscreenpane" style="width: 100%; height: 100%">
|
||||
<button id="dialog" (click)="openDialog()">DIALOG</button>
|
||||
<button id="exitfullscreen" (click)="exitFullscreen()">EXIT FULLSCREEN</button>
|
||||
</div>
|
||||
@@ -0,0 +1,61 @@
|
||||
import {Component, ElementRef, Output, EventEmitter} from '@angular/core';
|
||||
import {MdDialog, MdDialogRef} from '@angular/material';
|
||||
|
||||
@Component({
|
||||
selector: 'fullscreen-e2e',
|
||||
moduleId: module.id,
|
||||
templateUrl: 'fullscreen-e2e.html'
|
||||
})
|
||||
export class FullscreenE2E {
|
||||
dialogRef: MdDialogRef<TestDialog>;
|
||||
|
||||
constructor (private _element: ElementRef, private _dialog: MdDialog) { }
|
||||
|
||||
openDialog() {
|
||||
this.dialogRef = this._dialog.open(TestDialog);
|
||||
this.dialogRef.componentInstance.fullscreen.subscribe(() => this.toggleFullScreen());
|
||||
this.dialogRef.componentInstance.exitfullscreen.subscribe(() => this.exitFullscreen());
|
||||
this.dialogRef.afterClosed().subscribe(() => {
|
||||
this.dialogRef = null;
|
||||
});
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
let element = this._element.nativeElement.querySelector('#fullscreenpane');
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.webkitRequestFullScreen) {
|
||||
element.webkitRequestFullScreen();
|
||||
} else if ((element as any).mozRequestFullScreen) {
|
||||
(element as any).mozRequestFullScreen();
|
||||
} else if ((element as any).msRequestFullScreen) {
|
||||
(element as any).msRequestFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
exitFullscreen() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if ((document as any).mozExitFullScreen) {
|
||||
(document as any).mozExitFullScreen();
|
||||
} else if ((document as any).msExitFullScreen) {
|
||||
(document as any).msExitFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'fullscreen-dialog-e2e-test',
|
||||
template: `
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
|
||||
<button id="fullscreenindialog" (click)="fullscreen.emit()">FULLSCREEN</button>
|
||||
<button id="exitfullscreenindialog" (click)="exitfullscreen.emit()">EXIT FULLSCREEN</button>
|
||||
<button type="button" (click)="dialogRef.close()" id="close">CLOSE</button>`
|
||||
})
|
||||
export class TestDialog {
|
||||
constructor(public dialogRef: MdDialogRef<TestDialog>) { }
|
||||
@Output() fullscreen = new EventEmitter<void>();
|
||||
@Output() exitfullscreen = new EventEmitter<void>();
|
||||
}
|
||||
@@ -45,6 +45,7 @@ export {Platform as MdPlatform} from './platform/platform';
|
||||
// Overlay
|
||||
export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay';
|
||||
export {OverlayContainer} from './overlay/overlay-container';
|
||||
export {FullscreenOverlayContainer} from './overlay/fullscreen-overlay-container';
|
||||
export {OverlayRef} from './overlay/overlay-ref';
|
||||
export {OverlayState} from './overlay/overlay-state';
|
||||
export {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {OverlayContainer} from './overlay-container';
|
||||
|
||||
/**
|
||||
* The FullscreenOverlayContainer is the alternative to OverlayContainer
|
||||
* that supports correct displaying of overlay elements in Fullscreen mode
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen
|
||||
* It should be provided in the root component that way:
|
||||
* providers: [
|
||||
* {provide: OverlayContainer, useClass: FullscreenOverlayContainer}
|
||||
* ],
|
||||
*/
|
||||
@Injectable()
|
||||
export class FullscreenOverlayContainer extends OverlayContainer {
|
||||
protected _createContainer(): void {
|
||||
super._createContainer();
|
||||
this._adjustParentForFullscreenChange();
|
||||
this._addFullscreenChangeListener(() => this._adjustParentForFullscreenChange());
|
||||
}
|
||||
|
||||
private _adjustParentForFullscreenChange(): void {
|
||||
if (!this._containerElement) {
|
||||
return;
|
||||
}
|
||||
let fullscreenElement = this.getFullscreenElement();
|
||||
let parent = fullscreenElement || document.body;
|
||||
parent.appendChild(this._containerElement);
|
||||
}
|
||||
|
||||
private _addFullscreenChangeListener(fn: () => void) {
|
||||
if (document.fullscreenEnabled) {
|
||||
document.addEventListener('fullscreenchange', fn);
|
||||
} else if (document.webkitFullscreenEnabled) {
|
||||
document.addEventListener('webkitfullscreenchange', fn);
|
||||
} else if ((document as any).mozFullScreenEnabled) {
|
||||
document.addEventListener('mozfullscreenchange', fn);
|
||||
} else if ((document as any).msFullscreenEnabled) {
|
||||
document.addEventListener('MSFullscreenChange', fn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the page is put into fullscreen mode, a specific element is specified.
|
||||
* Only that element and its children are visible when in fullscreen mode.
|
||||
*/
|
||||
getFullscreenElement(): Element {
|
||||
return document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
(document as any).mozFullScreenElement ||
|
||||
(document as any).msFullscreenElement ||
|
||||
null;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Injectable()
|
||||
export class OverlayContainer {
|
||||
private _containerElement: HTMLElement;
|
||||
protected _containerElement: HTMLElement;
|
||||
|
||||
/**
|
||||
* This method returns the overlay container element. It will lazily
|
||||
@@ -20,7 +23,7 @@ export class OverlayContainer {
|
||||
* Create the overlay container element, which is simply a div
|
||||
* with the 'cdk-overlay-container' class on the document body.
|
||||
*/
|
||||
private _createContainer(): void {
|
||||
protected _createContainer(): void {
|
||||
let container = document.createElement('div');
|
||||
container.classList.add('cdk-overlay-container');
|
||||
document.body.appendChild(container);
|
||||
|
||||
Reference in New Issue
Block a user