diff --git a/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.html b/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.html index 440e6629..8fbb6790 100644 --- a/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.html +++ b/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.html @@ -1,31 +1,71 @@

- Open a dialog over the app's content. Press escape or click outside to close the dialog and - send focus back to the triggering button. + Open a dialog over the app's content. Press escape to close the dialog and send focus back to the triggering button.

- - Test Title - Test Content -
- - - + + + + -
- Custom Dialog Fullscreen - -
-
diff --git a/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.ts b/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.ts index 5081046c..0344b6a2 100644 --- a/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.ts +++ b/modules/docs/src/app/examples/dialog/dialog-basic-usage.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component} from '@angular/core'; import {MATERIAL_DIRECTIVES} from 'ng2-material'; @Component({ @@ -9,51 +9,21 @@ import {MATERIAL_DIRECTIVES} from 'ng2-material'; directives: [MATERIAL_DIRECTIVES] }) export class DialogBasicUsageComponent { -} + status: string = ''; -@Component({ - selector: 'dialog-custom', - template: ` -
-

{{fruit}} (Fruit)

-
-

- The mango is a juicy stone fruit belonging to the genus Mangifera, consisting of numerous - tropical fruiting trees, cultivated mostly for edible fruit. The majority of these species - are found in nature as wild mangoes. They all belong to the flowering plant family - Anacardiaceae. The mango is native to South and Southeast Asia, from where it has been - distributed worldwide to become one of the most cultivated fruits in the tropics. -

-

- The highest concentration of Mangifera genus is in the western part of Malesia (Sumatra, - Java and Borneo) and in Burma and India. While other Mangifera species (e.g. horse mango, - M. foetida) are also grown on a more localized basis, Mangifera indica—the "common - mango" or "Indian mango"—is the only mango tree commonly cultivated in many tropical - and subtropical regions. -

-

- It originated in Indian subcontinent (present day India and Pakistan) and Burma. It is the - national fruit of India, Pakistan, and the Philippines, and the national tree of - Bangladesh. In several cultures, its fruit and leaves are ritually used as floral - decorations at weddings, public celebrations, and religious ceremonies. -

-
- - - More on Wikipedia - - - - - -
- `, - directives: [MATERIAL_DIRECTIVES] -}) -class DialogCustom { - @Input() fruit: string; + confirmClose(forgiveDebt: boolean) { + if (forgiveDebt) { + this.status = 'You decided to get rid of your debt.'; + } else { + this.status = 'You decided to keep your debt.'; + } + } + + customClose(interesting: boolean) { + if (interesting) { + this.status = 'That article was interesting.'; + } else { + this.status = 'Look for something else.'; + } + } } diff --git a/modules/docs/src/app/examples/dialog/readme.md b/modules/docs/src/app/examples/dialog/readme.md index 0ab817aa..6947691d 100644 --- a/modules/docs/src/app/examples/dialog/readme.md +++ b/modules/docs/src/app/examples/dialog/readme.md @@ -1,4 +1,4 @@ ``` -This example is currently broken. +These components are a work in progress. ``` diff --git a/src/components/dialog/dialog-actions.scss b/src/components/dialog/dialog-actions.scss new file mode 100644 index 00000000..c3159d14 --- /dev/null +++ b/src/components/dialog/dialog-actions.scss @@ -0,0 +1,28 @@ +.md-dialog { + md-dialog-actions { + display: flex; + order: 2; + box-sizing: border-box; + align-items: center; + justify-content: flex-end; + padding-top: $baseline-grid * 3; + padding-right: $baseline-grid; + padding-left: $baseline-grid * 2; + + // Align md-actions outside of the padding of .md-dialog + margin-bottom: -$baseline-grid * 3; + margin-left: -$baseline-grid * 3; + margin-right: -$baseline-grid * 3; + + right: -$baseline-grid * 3; + min-height: $baseline-grid * 6.5; + overflow: hidden; + + [md-button], [md-raised-button] { + margin-bottom: $baseline-grid; + margin-left: $baseline-grid; + margin-right: 0; + margin-top: $baseline-grid; + } + } +} diff --git a/src/components/dialog/dialog-actions.ts b/src/components/dialog/dialog-actions.ts new file mode 100644 index 00000000..6935249f --- /dev/null +++ b/src/components/dialog/dialog-actions.ts @@ -0,0 +1,20 @@ +import {Component, Input, ChangeDetectionStrategy} from '@angular/core'; +import {MdDialog} from './dialog'; + +@Component({ + selector: 'md-dialog-actions', + template: ` + + + `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class MdDialogActions { + @Input() cancel: string; + @Input() ok: string; + @Input() dialog: MdDialog; +} diff --git a/src/components/dialog/dialog-portal.ts b/src/components/dialog/dialog-portal.ts new file mode 100644 index 00000000..237ea295 --- /dev/null +++ b/src/components/dialog/dialog-portal.ts @@ -0,0 +1,9 @@ +import {Directive, ViewContainerRef, TemplateRef} from '@angular/core'; +import {TemplatePortalDirective} from '@angular2-material/core'; + +@Directive({selector: '[mdDialogPortal]'}) +export class MdDialogPortal extends TemplatePortalDirective { + constructor(templateRef: TemplateRef, viewContainerRef: ViewContainerRef) { + super(templateRef, viewContainerRef); + } +} diff --git a/src/components/dialog/dialog-title.scss b/src/components/dialog/dialog-title.scss new file mode 100644 index 00000000..c2c6460d --- /dev/null +++ b/src/components/dialog/dialog-title.scss @@ -0,0 +1,8 @@ +@import "../../core/style/typography"; + +.md-dialog { + md-dialog-title { + @extend .md-headline; + margin-bottom: 20px; + } +} diff --git a/src/components/dialog/dialog-title.ts b/src/components/dialog/dialog-title.ts new file mode 100644 index 00000000..3a5e6b33 --- /dev/null +++ b/src/components/dialog/dialog-title.ts @@ -0,0 +1,11 @@ +import {Component, Input, ChangeDetectionStrategy} from '@angular/core'; +import {MdDialog} from './dialog'; + +@Component({ + selector: 'md-dialog-title', + template: `

{{title}}

`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class MdDialogTitle { + @Input() title: string; +} diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index de818a15..730a19d3 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -1,14 +1,12 @@ @import "../../core/style/variables"; @import "../../core/style/shadows"; @import "../../core/style/default-theme"; +@import "../../core/style/typography"; +md-dialog { + display: none; +} .md-dialog { - position: fixed; - z-index: 80; - - /** Center the dialog. */ - top: 50%; - left: 50%; min-width: 300px; min-height: 100px; @@ -20,62 +18,20 @@ opacity: 0; transition: $swift-ease-out; - transform: translate3d(-50%, -50%, 0) scale(0.2); + transform: scale(0.2); order: 1; overflow: auto; -webkit-overflow-scrolling: touch; - &:not([layout=row]) > * > *:first-child:not(.md-subheader) { - margin-top: 0; - } - &:focus { outline: none; } &.md-active { opacity: 1; - transition: $swift-ease-out; - transform: translate3d(-50%, -50%, 0) scale(1.0); - -webkit-filter: blur(0px); - -moz-filter: blur(0px); - -ms-filter: blur(0px); - filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='0'); - filter: none; - } - - &.md-dialog-absolute { - position: absolute; - } - - .md-actions, md-dialog-actions { - display: flex; - order: 2; - box-sizing: border-box; - align-items: center; - justify-content: flex-end; - padding-top: $baseline-grid * 3; - padding-right: $baseline-grid; - padding-left: $baseline-grid * 2; - - // Align md-actions outside of the padding of .md-dialog - margin-bottom: -$baseline-grid * 3; - margin-left: -$baseline-grid * 3; - margin-right: -$baseline-grid * 3; - - right: -$baseline-grid * 3; - min-height: $baseline-grid * 6.5; - overflow: hidden; - - [md-button], [md-raised-button] { - margin-bottom: $baseline-grid; - margin-left: $baseline-grid; - margin-right: 0; - margin-top: $baseline-grid; - } + transform: perspective(1px) scale(1.0); } - } // Theme @@ -93,4 +49,5 @@ $dialog-border-radius: 4px !default; } } - +@import "dialog-actions"; +@import "dialog-title"; diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index c76b36a9..f413f11b 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -1,47 +1,121 @@ -import {Component, Input, ChangeDetectionStrategy} from "@angular/core"; +import { + Component, + Output, + Input, + AfterContentInit, + ContentChild, + EventEmitter, + ViewChild, + ViewEncapsulation, + OnDestroy +} from '@angular/core'; +import {Overlay, OverlayState, OverlayRef} from '@angular2-material/core/overlay/overlay'; +import {Animate} from '../../core/util/animate'; +import {MdDialogPortal} from './dialog-portal'; +import {MdDialogActions} from './dialog-actions'; +import {MdDialogTitle} from './dialog-title'; +import {KeyCodes} from '../../core/key_codes'; +// TODO(jd): behavioral tests +// TODO(jd): backdrop and clickToClose options @Component({ - selector: 'md-dialog-title', - template: ``, - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'md-dialog', + directives: [MdDialogTitle, MdDialogActions, MdDialogPortal], + providers: [Overlay], + encapsulation: ViewEncapsulation.None, + template: ` + +`, + host: { + 'tabindex': '0', + '(body:keydown)': 'onDocumentKeypress($event)' + } }) -export class MdDialogTitle { -} +export class MdDialog implements AfterContentInit, OnDestroy { + constructor(private overlay: Overlay) { + this.config.positionStrategy = this.overlay.position() + .global() + .centerHorizontally() + .centerVertically(); + } -@Component({ - selector: 'md-dialog-actions', - template: ``, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class MdDialogActions { -} + @Output() onShow: EventEmitter = new EventEmitter(); + @Output() onClose: EventEmitter = new EventEmitter(); + @Output() onCancel: EventEmitter = new EventEmitter(); + /** The portal to send the dialog content through */ + @ViewChild(MdDialogPortal) private portal: MdDialogPortal; -@Component({ - selector: 'md-dialog', - directives: [MdDialogTitle, MdDialogActions], - template: `

{{ title }}

- - - - - - - -` -}) -export class MdDialog { - @Input() title: string = ''; - @Input() text: string = ''; - @Input() cancel: string = ''; - @Input() ok: string = ''; + /** Dialog actions */ + @ContentChild(MdDialogActions) private actions: MdDialogActions; + + /** Is the dialog active? */ + private active: boolean = false; + /** Overlay configuration for positioning the dialog */ + @Input() config = new OverlayState(); + + /** @internal */ + private overlayRef: OverlayRef = null; + + ngAfterContentInit(): any { + if (this.actions) { + this.actions.dialog = this; + } + } + + ngOnDestroy(): any { + return this.close(); + } + + /** Show the dialog */ show(): Promise { - return Promise.resolve(null); + return this.close() + .then(() => this.overlay.create(this.config)) + .then((ref: OverlayRef) => { + this.overlayRef = ref; + return ref.attach(this.portal); + }) + .then(() => Animate.wait()) + .then(() => { + this.active = true; + this.onShow.emit(this); + return this; + }); + } + + /** Close the dialog */ + close(result: any = true, cancel: boolean = false): Promise { + if (!this.overlayRef) { + return Promise.resolve(this); + } + this.active = false; + // TODO(jd): this is terrible, use animate states + return Animate.wait(100) + .then(() => this.overlayRef.detach()) + .then(() => { + this.overlayRef.dispose(); + this.overlayRef = null; + if (cancel) { + this.onCancel.emit(result); + } + else { + this.onClose.emit(result); + } + return this; + }); + } + + private onDocumentKeypress(event: KeyboardEvent) { + if (event.keyCode == KeyCodes.ESCAPE) { + this.close(); + } } } diff --git a/src/components/dialog/index.ts b/src/components/dialog/index.ts new file mode 100644 index 00000000..1e9eb43a --- /dev/null +++ b/src/components/dialog/index.ts @@ -0,0 +1,4 @@ +export * from './dialog'; +export * from './dialog-actions'; +export * from './dialog-portal'; +export * from './dialog-title'; diff --git a/src/components/dialog/readme.md b/src/components/dialog/readme.md new file mode 100644 index 00000000..324319c7 --- /dev/null +++ b/src/components/dialog/readme.md @@ -0,0 +1,57 @@ +# MdDialog +Dialogs allow prompting the user to make a decision or take action that must be completed before normal operation may continue. + +### Examples +A simple alert dialog +```html + + Clicking the button will close this dialog + + + +``` + +A confirmation dialog with a yes/no decision +```html + + Are you sure? + This decision will change your life. + + + +``` + +## `` +### Properties + +| Name | Type | Description | +| --- | --- | --- | +| `config` | `OverlayConfig` | Used to override dialog positioning | + + +### Events + +| Name | Value Type | Description | +| --- | --- | --- | +| `onClose` | `any` | Emitted when the dialog closes with a user specified value | +| `onCancel` | `any` | Emitted when the dialog closes because of an escape action | +| `onShow` | `MdDialog` | The user specified return value from the dialog | + + +## `` +### Properties + +| Name | Type | Description | +| --- | --- | --- | +| `title` | `string` | Specify dialog title with a binding | + + +## `` +### Properties + +| Name | Type | Description | +| --- | --- | --- | +| `ok` | `string` | The label to use for an acceptance action button | +| `cancel` | `string` | The label to use for a cancel action button | +| `dialog` | `MdDialog` | The dialog to take action on. Set by the owning dialog, but could be overridden for custom behavior. | + diff --git a/src/index.ts b/src/index.ts index f4ad1b07..bbc9e0ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ import { MdDataTableHeaderSelectableRow, MdDataTableSelectableRow } from "./components/data_table/data_table"; -import {MdDialog, MdDialogTitle, MdDialogActions} from "./components/dialog/dialog"; +import {MdDialog, MdDialogTitle, MdDialogActions, MdDialogPortal} from "./components/dialog/index"; import {MdDivider} from "./components/divider/divider"; import {MdIcon} from "./components/icon/icon"; import {MdInk} from "./components/ink/ink"; @@ -28,14 +28,15 @@ import {Media} from "./core/util/media"; import {ViewportHelper, BrowserViewportHelper, NodeViewportHelper} from "./core/util/viewport"; import {OVERLAY_CONTAINER_TOKEN, createOverlayContainer} from "@angular2-material/core/overlay/overlay"; import {MdBackdrop} from "./components/backdrop/backdrop"; + export * from './components/button/button'; -export * from './components/backdrop/backdrop'; +export * from './components/backdrop/backdrop'; export * from './components/content/content'; export * from './components/data_table/data_table'; -export * from './components/dialog/dialog'; +export * from './components/dialog/index'; export * from './components/divider/divider'; export * from './components/icon/icon'; @@ -70,7 +71,7 @@ export const MATERIAL_DIRECTIVES: any[] = [ MdDataTable, MdDataTableHeaderSelectableRow, MdDataTableSelectableRow, MdDivider, MdBackdrop, - MdDialog, MdDialogActions, MdDialogTitle, + MdDialog, MdDialogActions, MdDialogTitle, MdDialogPortal, MdIcon, MdInk, MdPatternValidator, MdMaxLengthValidator, diff --git a/tsconfig.json b/tsconfig.json index 64c09102..79e7ac97 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "noImplicitAny": false, "outDir": "dist/", "rootDir": "src/", - "inlineSourceMap": true, + "sourceMap": true, "target": "es5" }, "filesGlob": [ @@ -28,11 +28,15 @@ "src/components/card/card_spec.ts", "src/components/content/content.ts", "src/components/content/content_spec.ts", - "src/components/data_table/data_table_spec.ts", "src/components/data_table/data_table.ts", "src/components/data_table/data_table_selectable_tr.ts", + "src/components/data_table/data_table_spec.ts", + "src/components/dialog/dialog-actions.ts", + "src/components/dialog/dialog-portal.ts", + "src/components/dialog/dialog-title.ts", "src/components/dialog/dialog.ts", "src/components/dialog/dialog_spec.ts", + "src/components/dialog/index.ts", "src/components/divider/divider.ts", "src/components/divider/divider_spec.ts", "src/components/form/messages.ts", @@ -64,4 +68,4 @@ "src/platform/testing/test_url_resolver.ts", "src/platform/testing/util.ts" ] -} +} \ No newline at end of file