Skip to content

feat(dialog): add configurable width, height and position #1848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/demo-app/dialog/dialog-demo.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
<h1>Dialog demo</h1>

<button (click)="open()" [disabled]="dialogRef">Open dialog</button>
<button md-raised-button color="primary" (click)="open()" [disabled]="dialogRef">Open dialog</button>

<p>
Last close result: {{lastCloseResult}}
</p>
<md-card class="demo-dialog-card">
<md-card-content>
<h2>Dialog dimensions</h2>

<p>
<md-input [(ngModel)]="config.width" placeholder="Width"></md-input>
<md-input [(ngModel)]="config.height" placeholder="Height"></md-input>
</p>

<h2>Dialog position</h2>

<p>
<md-input [(ngModel)]="config.position.top" (change)="config.position.bottom = ''" placeholder="Top"></md-input>
<md-input [(ngModel)]="config.position.bottom" (change)="config.position.top = ''" placeholder="Bottom"></md-input>
</p>

<p>
<md-input [(ngModel)]="config.position.left" (change)="config.position.right = ''" placeholder="Left"></md-input>
<md-input [(ngModel)]="config.position.right" (change)="config.position.left = ''" placeholder="Right"></md-input>
</p>

<h2>Other options</h2>

<md-checkbox [(ngModel)]="config.disableClose">Disable close</md-checkbox>
</md-card-content>
</md-card>

<p>Last close result: {{lastCloseResult}}</p>
5 changes: 5 additions & 0 deletions src/demo-app/dialog/dialog-demo.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.demo-dialog {
color: rebeccapurple;
}

.demo-dialog-card {
max-width: 350px;
margin: 20px 0;
}
15 changes: 13 additions & 2 deletions src/demo-app/dialog/dialog-demo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Component} from '@angular/core';
import {MdDialog, MdDialogRef} from '@angular/material';
import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';

@Component({
moduleId: module.id,
Expand All @@ -10,11 +10,22 @@ import {MdDialog, MdDialogRef} from '@angular/material';
export class DialogDemo {
dialogRef: MdDialogRef<JazzDialog>;
lastCloseResult: string;
config: MdDialogConfig = {
disableClose: false,
width: '',
height: '',
position: {
top: '',
bottom: '',
left: '',
right: ''
}
};

constructor(public dialog: MdDialog) { }

open() {
this.dialogRef = this.dialog.open(JazzDialog);
this.dialogRef = this.dialog.open(JazzDialog, this.config);

this.dialogRef.afterClosed().subscribe(result => {
this.lastCloseResult = result;
Expand Down
34 changes: 34 additions & 0 deletions src/lib/core/overlay/position/global-position-strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,40 @@ describe('GlobalPositonStrategy', () => {

expect(element.style.position).toBe('fixed');
}));

it('should set the element width', fakeAsync(() => {
strategy.width('100px').apply(element);

flushMicrotasks();

expect(element.style.width).toBe('100px');
}));

it('should set the element height', fakeAsync(() => {
strategy.height('100px').apply(element);

flushMicrotasks();

expect(element.style.height).toBe('100px');
}));

it('should reset the horizontal position and offset when the width is 100%', fakeAsync(() => {
strategy.centerHorizontally().width('100%').apply(element);

flushMicrotasks();

expect(element.style.left).toBe('0px');
expect(element.style.transform).toBe('');
}));

it('should reset the vertical position and offset when the height is 100%', fakeAsync(() => {
strategy.centerVertically().height('100%').apply(element);

flushMicrotasks();

expect(element.style.top).toBe('0px');
expect(element.style.transform).toBe('');
}));
});

function fakeAsyncTest(fn: () => void) {
Expand Down
52 changes: 46 additions & 6 deletions src/lib/core/overlay/position/global-position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export class GlobalPositionStrategy implements PositionStrategy {
private _bottom: string = '';
private _left: string = '';
private _right: string = '';
private _width: string = '';
private _height: string = '';

/** Array of individual applications of translateX(). Currently only for centering. */
private _translateX: string[] = [];
Expand Down Expand Up @@ -63,25 +65,61 @@ export class GlobalPositionStrategy implements PositionStrategy {
return this;
}

/** Sets the overlay width and clears any previously set width. */
width(value: string) {
this._width = value;

// When the width is 100%, we should reset the `left` and the offset,
// in order to ensure that the element is flush against the viewport edge.
if (value === '100%') {
this.left('0px');
}

return this;
}

/** Sets the overlay height and clears any previously set height. */
height(value: string) {
this._height = value;

// When the height is 100%, we should reset the `top` and the offset,
// in order to ensure that the element is flush against the viewport edge.
if (value === '100%') {
this.top('0px');
}

return this;
}

/**
* Centers the overlay horizontally with an optional offset.
* Clears any previously set horizontal position.
*/
centerHorizontally(offset = '0px') {
centerHorizontally(offset = '') {
this._left = '50%';
this._right = '';
this._translateX = ['-50%', offset];
this._translateX = ['-50%'];

if (offset) {
this._translateX.push(offset);
}

return this;
}

/**
* Centers the overlay vertically with an optional offset.
* Clears any previously set vertical position.
*/
centerVertically(offset = '0px') {
centerVertically(offset = '') {
this._top = '50%';
this._bottom = '';
this._translateY = ['-50%', offset];
this._translateY = ['-50%'];

if (offset) {
this._translateY.push(offset);
}

return this;
}

Expand All @@ -95,13 +133,15 @@ export class GlobalPositionStrategy implements PositionStrategy {
element.style.left = this._left;
element.style.bottom = this._bottom;
element.style.right = this._right;
element.style.width = this._width;
element.style.height = this._height;

// TODO(jelbourn): we don't want to always overwrite the transform property here,
// because it will need to be used for animations.
let tranlateX = this._reduceTranslateValues('translateX', this._translateX);
let translateX = this._reduceTranslateValues('translateX', this._translateX);
let translateY = this._reduceTranslateValues('translateY', this._translateY);

applyCssTransform(element, `${tranlateX} ${translateY}`);
applyCssTransform(element, `${translateX} ${translateY}`);

return Promise.resolve(null);
}
Expand Down
7 changes: 5 additions & 2 deletions src/lib/dialog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ MdDialog is a service, which opens dialogs components in the view.

| Key | Description |
| --- | --- |
| `role: DialogRole = 'dialog'` | The ARIA role of the dialog element. Possible values are `dialog` and `alertdialog`. Defaults to `dialog`. |
| `disableClose: boolean = false` | Whether to prevent the user from closing a dialog by clicking on the backdrop or pressing escape. Defaults to `false`. |
| `role: DialogRole = 'dialog'` | The ARIA role of the dialog element. Possible values are `dialog` and `alertdialog`. Optional. |
| `disableClose: boolean = false` | Whether to prevent the user from closing a dialog by clicking on the backdrop or pressing escape. Optional. |
| `width: string = ''` | Width of the dialog. Takes any valid CSS value. Optional. |
| `height: string = ''` | Height of the dialog. Takes any valid CSS value. Optional. |
| `position: { top?: string, bottom?: string, left?: string, right?: string }` | Position of the dialog that overrides the default centering in it's axis. Optional. |
| `viewContainerRef: ViewContainerRef` | The view container ref to attach the dialog to. Optional. |

## MdDialogRef
Expand Down
20 changes: 18 additions & 2 deletions src/lib/dialog/dialog-config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import {ViewContainerRef} from '@angular/core';

/** Valid ARIA roles for a dialog element. */
export type DialogRole = 'dialog' | 'alertdialog'
export type DialogRole = 'dialog' | 'alertdialog';

/** Possible overrides for a dialog's position. */
export interface DialogPosition {
top?: string;
bottom?: string;
left?: string;
right?: string;
};


/**
Expand All @@ -17,5 +24,14 @@ export class MdDialogConfig {
/** Whether the user can use escape or clicking outside to close a modal. */
disableClose?: boolean = false;

// TODO(jelbourn): add configuration for size, lifecycle hooks, ARIA labelling.
/** Width of the dialog. */
width?: string = '';

/** Height of the dialog. */
height?: string = '';

/** Position overrides. */
position?: DialogPosition;

// TODO(jelbourn): add configuration for lifecycle hooks, ARIA labelling.
}
7 changes: 6 additions & 1 deletion src/lib/dialog/dialog-container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ md-dialog-container {
@include md-elevation(24);

display: block;
overflow: hidden;
padding: $md-dialog-padding;
border-radius: $md-dialog-border-radius;
box-sizing: border-box;
overflow: auto;

// The dialog container should completely fill its parent overlay element.
width: 100%;
height: 100%;
}
80 changes: 80 additions & 0 deletions src/lib/dialog/dialog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,86 @@ describe('MdDialog', () => {
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeFalsy();
});

it('should should override the width of the overlay pane', () => {
dialog.open(PizzaMsg, {
width: '500px'
});

viewContainerFixture.detectChanges();

let overlayPane = overlayContainerElement.querySelector('.md-overlay-pane') as HTMLElement;

expect(overlayPane.style.width).toBe('500px');
});

it('should should override the height of the overlay pane', () => {
dialog.open(PizzaMsg, {
height: '100px'
});

viewContainerFixture.detectChanges();

let overlayPane = overlayContainerElement.querySelector('.md-overlay-pane') as HTMLElement;

expect(overlayPane.style.height).toBe('100px');
});

it('should should override the top offset of the overlay pane', () => {
dialog.open(PizzaMsg, {
position: {
top: '100px'
}
});

viewContainerFixture.detectChanges();

let overlayPane = overlayContainerElement.querySelector('.md-overlay-pane') as HTMLElement;

expect(overlayPane.style.top).toBe('100px');
});

it('should should override the bottom offset of the overlay pane', () => {
dialog.open(PizzaMsg, {
position: {
bottom: '200px'
}
});

viewContainerFixture.detectChanges();

let overlayPane = overlayContainerElement.querySelector('.md-overlay-pane') as HTMLElement;

expect(overlayPane.style.bottom).toBe('200px');
});

it('should should override the left offset of the overlay pane', () => {
dialog.open(PizzaMsg, {
position: {
left: '250px'
}
});

viewContainerFixture.detectChanges();

let overlayPane = overlayContainerElement.querySelector('.md-overlay-pane') as HTMLElement;

expect(overlayPane.style.left).toBe('250px');
});

it('should should override the right offset of the overlay pane', () => {
dialog.open(PizzaMsg, {
position: {
right: '125px'
}
});

viewContainerFixture.detectChanges();

let overlayPane = overlayContainerElement.querySelector('.md-overlay-pane') as HTMLElement;

expect(overlayPane.style.right).toBe('125px');
});

describe('disableClose option', () => {
it('should prevent closing via clicks on the backdrop', () => {
dialog.open(PizzaMsg, {
Expand Down
23 changes: 17 additions & 6 deletions src/lib/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ export {MdDialogRef} from './dialog-ref';

// TODO(jelbourn): add support for opening with a TemplateRef
// TODO(jelbourn): add `closeAll` method
// TODO(jelbourn): default dialog config
// TODO(jelbourn): escape key closes dialog
// TODO(jelbourn): dialog content directives (e.g., md-dialog-header)
// TODO(jelbourn): animations

Expand Down Expand Up @@ -119,12 +117,25 @@ export class MdDialog {
*/
private _getOverlayState(dialogConfig: MdDialogConfig): OverlayState {
let state = new OverlayState();
let strategy = this._overlay.position().global();
let position = dialogConfig.position;

state.hasBackdrop = true;
state.positionStrategy = this._overlay.position()
.global()
.centerHorizontally()
.centerVertically();
state.positionStrategy = strategy;

if (position && (position.left || position.right)) {
position.left ? strategy.left(position.left) : strategy.right(position.right);
} else {
strategy.centerHorizontally();
}

if (position && (position.top || position.bottom)) {
position.top ? strategy.top(position.top) : strategy.bottom(position.bottom);
} else {
strategy.centerVertically();
}

strategy.width(dialogConfig.width).height(dialogConfig.height);

return state;
}
Expand Down