-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(modal): add initial version of the modal service
Closes #3
- Loading branch information
1 parent
4115ee4
commit 3ef99c5
Showing
24 changed files
with
796 additions
and
80 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
demo/src/app/components/modal/demos/basic/modal-basic.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template #content let-c="close" let-d="dismiss"> | ||
<div class="modal-header"> | ||
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')"> | ||
<span aria-hidden="true">×</span> | ||
</button> | ||
<h4 class="modal-title">Modal title</h4> | ||
</div> | ||
<div class="modal-body"> | ||
<p>One fine body…</p> | ||
</div> | ||
<div class="modal-footer"> | ||
<button type="button" class="btn btn-secondary" (click)="c('Close click')">Close</button> | ||
</div> | ||
</template> | ||
|
||
<button class="btn btn-lg btn-outline-primary" (click)="open(content)">Launch demo modal</button> | ||
|
||
<hr> | ||
|
||
<pre>{{closeResult}}</pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import {Component} from '@angular/core'; | ||
|
||
import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap'; | ||
|
||
@Component({ | ||
selector: 'ngbd-modal-basic', | ||
templateUrl: './modal-basic.html' | ||
}) | ||
export class NgbdModalBasic { | ||
closeResult: string; | ||
|
||
constructor(private modalService: NgbModal) {} | ||
|
||
open(content) { | ||
this.modalService.open(content).result.then((result) => { | ||
this.closeResult = `Closed with: ${result}`; | ||
}, (reason) => { | ||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`; | ||
}); | ||
} | ||
|
||
private getDismissReason(reason: any): string { | ||
if (reason === ModalDismissReasons.ESC) { | ||
return 'by pressing ESC'; | ||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) { | ||
return 'by clicking on a backdrop'; | ||
} else { | ||
return `with: ${reason}`; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import {NgbdModalBasic} from './basic/modal-basic'; | ||
|
||
export const DEMO_DIRECTIVES = [NgbdModalBasic]; | ||
|
||
export const DEMO_SNIPPETS = { | ||
basic: { | ||
code: require('!!prismjs?lang=typescript!./basic/modal-basic'), | ||
markup: require('!!prismjs?lang=markup!./basic/modal-basic.html')} | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
export * from './modal.component'; | ||
|
||
import {NgModule} from '@angular/core'; | ||
import {CommonModule} from '@angular/common'; | ||
|
||
import {NgbdModal} from './modal.component'; | ||
import {NgbdSharedModule} from '../../shared'; | ||
import {NgbdComponentsSharedModule} from '../shared'; | ||
import {DEMO_DIRECTIVES} from './demos'; | ||
|
||
@NgModule({ | ||
imports: [CommonModule], | ||
imports: [NgbdSharedModule, NgbdComponentsSharedModule], | ||
exports: [NgbdModal], | ||
declarations: [NgbdModal] | ||
declarations: [NgbdModal, ...DEMO_DIRECTIVES] | ||
}) | ||
export class NgbdModalModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,25 @@ | ||
import {Component} from '@angular/core'; | ||
|
||
import {DEMO_SNIPPETS} from './demos'; | ||
|
||
@Component({ | ||
selector: 'ngbd-modal', | ||
template: ` | ||
<ngbd-api-docs directive="NgbModal"></ngbd-api-docs> | ||
<template ngbModalContainer></template> | ||
<ngbd-content-wrapper component="Modal"> | ||
<ngbd-api-docs directive="NgbModal"></ngbd-api-docs> | ||
<ngb-alert [dismissible]="false"> | ||
<strong>Heads up!</strong> | ||
The <code>NgbModal</code> service needs a container element with the <code>ngbModalContainer</code> directive. The | ||
<code>ngbModalContainer</code> directive marks the place in the DOM where modals are opened. Be sure to add | ||
<code><template ngbModalContainer></template></code> somewhere under your application root element. | ||
</ngb-alert> | ||
<ngbd-example-box demoTitle="Modal with default options" [htmlSnippet]="snippets.basic.markup" [tsSnippet]="snippets.basic.code"> | ||
<ngbd-modal-basic></ngbd-modal-basic> | ||
</ngbd-example-box> | ||
</ngbd-content-wrapper> | ||
` | ||
}) | ||
export class NgbdModal {} | ||
export class NgbdModal { | ||
snippets = DEMO_SNIPPETS; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/modal/modal_backdrop.spec.ts → src/modal/modal-backdrop.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import {Component} from '@angular/core'; | ||
|
||
@Component({selector: 'ngb-modal-backdrop', template: '', host: {'class': 'modal-backdrop'}}) | ||
@Component({selector: 'ngb-modal-backdrop', template: '', host: {'class': 'modal-backdrop fade in'}}) | ||
export class NgbModalBackdrop { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { | ||
Directive, | ||
Injector, | ||
Renderer, | ||
TemplateRef, | ||
ViewContainerRef, | ||
ComponentFactoryResolver, | ||
ComponentFactory, | ||
ComponentRef | ||
} from '@angular/core'; | ||
|
||
import {isDefined} from '../util/util'; | ||
|
||
import {NgbModalBackdrop} from './modal-backdrop'; | ||
import {NgbModalWindow} from './modal-window'; | ||
import {NgbModalStack} from './modal-stack'; | ||
import {NgbModalRef} from './modal-ref'; | ||
|
||
class ModalContentContext { | ||
close(result?: any) {} | ||
dismiss(reason?: any) {} | ||
} | ||
|
||
@Directive({selector: 'template[ngbModalContainer]'}) | ||
export class NgbModalContainer { | ||
private _backdropFactory: ComponentFactory<NgbModalBackdrop>; | ||
private _windowFactory: ComponentFactory<NgbModalWindow>; | ||
|
||
constructor( | ||
private _injector: Injector, private _renderer: Renderer, private _viewContainerRef: ViewContainerRef, | ||
componentFactoryResolver: ComponentFactoryResolver, ngbModalStack: NgbModalStack) { | ||
this._backdropFactory = componentFactoryResolver.resolveComponentFactory(NgbModalBackdrop); | ||
this._windowFactory = componentFactoryResolver.resolveComponentFactory(NgbModalWindow); | ||
|
||
ngbModalStack.registerContainer(this); | ||
} | ||
|
||
open(content: string | TemplateRef<any>, options): NgbModalRef { | ||
const modalContentContext = new ModalContentContext(); | ||
const nodes = this._getContentNodes(content, modalContentContext); | ||
const windowCmptRef = this._viewContainerRef.createComponent(this._windowFactory, 0, this._injector, nodes); | ||
let backdropCmptRef: ComponentRef<NgbModalBackdrop>; | ||
let ngbModalRef: NgbModalRef; | ||
|
||
if (options.backdrop !== false) { | ||
backdropCmptRef = this._viewContainerRef.createComponent(this._backdropFactory, 0, this._injector); | ||
} | ||
ngbModalRef = new NgbModalRef(this._viewContainerRef, windowCmptRef, backdropCmptRef); | ||
|
||
modalContentContext.close = (result: any) => { ngbModalRef.close(result); }; | ||
modalContentContext.dismiss = (reason: any) => { ngbModalRef.dismiss(reason); }; | ||
|
||
this._applyWindowOptions(windowCmptRef.instance, options); | ||
|
||
return ngbModalRef; | ||
} | ||
|
||
private _applyWindowOptions(windowInstance: NgbModalWindow, options: Object): void { | ||
['backdrop', 'keyboard', 'size'].forEach((optionName: string) => { | ||
if (isDefined(options[optionName])) { | ||
windowInstance[optionName] = options[optionName]; | ||
} | ||
}); | ||
} | ||
|
||
private _getContentNodes(content: string | TemplateRef<any>, context: ModalContentContext): any[] { | ||
if (!content) { | ||
return []; | ||
} else if (content instanceof TemplateRef) { | ||
return [this._viewContainerRef.createEmbeddedView(<TemplateRef<ModalContentContext>>content, context).rootNodes]; | ||
} else { | ||
return [[this._renderer.createText(null, `${content}`)]]; | ||
} | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import {ComponentRef, ViewContainerRef} from '@angular/core'; | ||
|
||
import {NgbModalBackdrop} from './modal-backdrop'; | ||
import {NgbModalWindow} from './modal-window'; | ||
|
||
/** | ||
* A reference to a newly opened modal. | ||
*/ | ||
export class NgbModalRef { | ||
private _resolve: (result?: any) => void; | ||
private _reject: (reason?: any) => void; | ||
|
||
/** | ||
* A promise that is resolved when a modal is closed and rejected when a modal is dismissed. | ||
*/ | ||
result: Promise<any>; | ||
|
||
constructor( | ||
private _viewContainerRef: ViewContainerRef, private _windowCmptRef: ComponentRef<NgbModalWindow>, | ||
private _backdropCmptRef?: ComponentRef<NgbModalBackdrop>) { | ||
_windowCmptRef.instance.dismissEvent.subscribe((reason: any) => { this.dismiss(reason); }); | ||
|
||
this.result = new Promise((resolve, reject) => { | ||
this._resolve = resolve; | ||
this._reject = reject; | ||
}); | ||
} | ||
|
||
/** | ||
* Can be used to close a modal, passing an optional result. | ||
*/ | ||
close(result?: any) { | ||
if (this._windowCmptRef) { | ||
this._resolve(result); | ||
this._removeModalElements(); | ||
} | ||
} | ||
|
||
/** | ||
* Can be used to dismiss a modal, passing an optional reason. | ||
*/ | ||
dismiss(reason?: any) { | ||
if (this._windowCmptRef) { | ||
this._reject(reason); | ||
this._removeModalElements(); | ||
} | ||
} | ||
|
||
private _removeModalElements() { | ||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._windowCmptRef.hostView)); | ||
if (this._backdropCmptRef) { | ||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._backdropCmptRef.hostView)); | ||
} | ||
|
||
this._windowCmptRef = null; | ||
this._backdropCmptRef = null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import {NgbModalStack} from './modal-stack'; | ||
|
||
describe('modal stack', () => { | ||
|
||
it('should throw if a container element was not registered', () => { | ||
const modalStack = new NgbModalStack(); | ||
expect(() => { modalStack.open('foo'); }).toThrowError(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import {Injectable, TemplateRef} from '@angular/core'; | ||
|
||
import {NgbModalRef} from './modal-ref'; | ||
import {NgbModalContainer} from './modal-container'; | ||
|
||
@Injectable() | ||
export class NgbModalStack { | ||
private modalContainer: NgbModalContainer; | ||
|
||
open(content: string | TemplateRef<any>, options = {}): NgbModalRef { | ||
if (!this.modalContainer) { | ||
throw new Error( | ||
'Missing modal container, add <template ngbModalContainer></template> to one of your application templates.'); | ||
} | ||
|
||
return this.modalContainer.open(content, options); | ||
} | ||
|
||
registerContainer(modalContainer: NgbModalContainer) { this.modalContainer = modalContainer; } | ||
} |
Oops, something went wrong.
3ef99c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When will this be inside a version which can be grapped from npm, just to be curious
3ef99c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As soon as Angular 2 rc.6 is out we are going to cut alpha.3 release with the modal service.
3ef99c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@masaanli we've just release alpha.3 with the modal service: https://ng-bootstrap.github.io/#/components/modal
3ef99c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pkozlowski-opensource Thank you, gonna implementate it! And maybe giving back some suggestions or thing where I'm stuggling about.