@nebular/theme
Version:
@nebular/theme
231 lines • 8.98 kB
JavaScript
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ComponentFactoryResolver, Inject, Injectable, Injector, TemplateRef } from '@angular/core';
import { fromEvent as observableFromEvent } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { NbComponentPortal, NbPortalInjector, NbTemplatePortal, } from '../cdk/overlay/mapping';
import { NbPositionBuilderService } from '../cdk/overlay/overlay-position';
import { NbOverlayService } from '../cdk/overlay/overlay-service';
import { NB_DOCUMENT } from '../../theme.options';
import { NB_DIALOG_CONFIG, NbDialogConfig } from './dialog-config';
import { NbDialogRef } from './dialog-ref';
import { NbDialogContainerComponent } from './dialog-container';
/**
* The `NbDialogService` helps to open dialogs.
*
* @stacked-example(Showcase, dialog/dialog-showcase.component)
*
* A new dialog is opened by calling the `open` method with a component to be loaded and an optional configuration.
* `open` method will return `NbDialogRef` that can be used for the further manipulations.
*
* ### Installation
*
* Import `NbDialogModule.forRoot()` to your app module.
* ```ts
* @NgModule({
* imports: [
* // ...
* NbDialogModule.forRoot(config),
* ],
* })
* export class AppModule { }
* ```
*
* If you are using it in a lazy loaded module than you have to install it with `NbDialogModule.forChild()`:
* ```ts
* @NgModule({
* imports: [
* // ...
* NbDialogModule.forChild(config),
* ],
* })
* export class LazyLoadedModule { }
* ```
*
* ### Usage
*
* ```ts
* const dialogRef = this.dialogService.open(MyDialogComponent, { ... });
* ```
*
* `NbDialogRef` gives capability access reference to the rendered dialog component,
* destroy dialog and some other options described below.
*
* Also, you can inject `NbDialogRef` in dialog component.
*
* ```ts
* this.dialogService.open(MyDialogComponent, { ... });
*
* // my-dialog.component.ts
* constructor(protected dialogRef: NbDialogRef) {
* }
*
* close() {
* this.dialogRef.close();
* }
* ```
*
* Instead of component you can create dialog from TemplateRef:
*
* @stacked-example(Template ref, dialog/dialog-template.component)
*
* The dialog may return result through `NbDialogRef`. Calling component can receive this result with `onClose`
* stream of `NbDialogRef`.
*
* @stacked-example(Result, dialog/dialog-result.component)
*
* ### Configuration
*
* As we mentioned above, `open` method of the `NbDialogService` may receive optional configuration options.
* Also, you can provide global dialogs configuration through `NbDialogModule.forRoot({ ... })`.
*
* This config may contain the following:
*
* `context` - both, template and component may receive data through `config.context` property.
* For components, this data will be assigned through inputs.
* For templates, you can access it inside template as $implicit.
*
* ```ts
* this.dialogService.open(template, { context: 'pass data in template' });
* ```
*
* ```html
* <ng-template let-some-additional-data>
* {{ some-additional-data }}
* <ng-template/>
* ```
*
* `hasBackdrop` - determines is service have to render backdrop under the dialog.
* Default is true.
* @stacked-example(Backdrop, dialog/dialog-has-backdrop.component)
*
* `closeOnBackdropClick` - close dialog on backdrop click if true.
* Default is true.
* @stacked-example(Backdrop click, dialog/dialog-backdrop-click.component)
*
* `closeOnEsc` - close dialog on escape button on the keyboard.
* Default is true.
* @stacked-example(Escape hit, dialog/dialog-esc.component)
*
* `hasScroll` - Disables scroll on content under dialog if true and does nothing otherwise.
* Default is false.
* Please, open dialogs in the separate window and try to scroll.
* @stacked-example(Scroll, dialog/dialog-scroll.component)
*
* `autoFocus` - Focuses dialog automatically after open if true. It's useful to prevent misclicks on
* trigger elements and opening multiple dialogs.
* Default is true.
*
* As you can see, if you open dialog with auto focus dialog will focus first focusable element
* or just blur previously focused automatically.
* Otherwise, without auto focus, the focus will stay on the previously focused element.
* Please, open dialogs in the separate window and try to click on the button without focus
* and then hit space any times. Multiple same dialogs will be opened.
* @stacked-example(Auto focus, dialog/dialog-auto-focus.component)
* */
export class NbDialogService {
constructor(document, globalConfig, positionBuilder, overlay, injector, cfr) {
this.document = document;
this.globalConfig = globalConfig;
this.positionBuilder = positionBuilder;
this.overlay = overlay;
this.injector = injector;
this.cfr = cfr;
}
/**
* Opens new instance of the dialog, may receive optional config.
* */
open(content, userConfig = {}) {
const config = new NbDialogConfig(Object.assign(Object.assign({}, this.globalConfig), userConfig));
const overlayRef = this.createOverlay(config);
const dialogRef = new NbDialogRef(overlayRef);
const container = this.createContainer(config, overlayRef);
this.createContent(config, content, container, dialogRef);
this.registerCloseListeners(config, overlayRef, dialogRef);
return dialogRef;
}
createOverlay(config) {
const positionStrategy = this.createPositionStrategy();
const scrollStrategy = this.createScrollStrategy(config.hasScroll);
return this.overlay.create({
positionStrategy,
scrollStrategy,
hasBackdrop: config.hasBackdrop,
backdropClass: config.backdropClass,
panelClass: config.dialogClass,
});
}
createPositionStrategy() {
return this.positionBuilder
.global()
.centerVertically()
.centerHorizontally();
}
createScrollStrategy(hasScroll) {
if (hasScroll) {
return this.overlay.scrollStrategies.noop();
}
else {
return this.overlay.scrollStrategies.block();
}
}
createContainer(config, overlayRef) {
const injector = new NbPortalInjector(this.createInjector(config), new WeakMap([[NbDialogConfig, config]]));
const containerPortal = new NbComponentPortal(NbDialogContainerComponent, null, injector, this.cfr);
const containerRef = overlayRef.attach(containerPortal);
return containerRef.instance;
}
createContent(config, content, container, dialogRef) {
if (content instanceof TemplateRef) {
const portal = this.createTemplatePortal(config, content, dialogRef);
container.attachTemplatePortal(portal);
}
else {
const portal = this.createComponentPortal(config, content, dialogRef);
dialogRef.componentRef = container.attachComponentPortal(portal);
if (config.context) {
Object.assign(dialogRef.componentRef.instance, Object.assign({}, config.context));
}
}
}
createTemplatePortal(config, content, dialogRef) {
return new NbTemplatePortal(content, null, { $implicit: config.context, dialogRef });
}
/**
* We're creating portal with custom injector provided through config or using global injector.
* This approach provides us capability inject `NbDialogRef` in dialog component.
* */
createComponentPortal(config, content, dialogRef) {
const injector = this.createInjector(config);
const portalInjector = new NbPortalInjector(injector, new WeakMap([[NbDialogRef, dialogRef]]));
return new NbComponentPortal(content, config.viewContainerRef, portalInjector);
}
createInjector(config) {
return config.viewContainerRef && config.viewContainerRef.injector || this.injector;
}
registerCloseListeners(config, overlayRef, dialogRef) {
if (config.closeOnBackdropClick) {
overlayRef.backdropClick().subscribe(() => dialogRef.close());
}
if (config.closeOnEsc) {
observableFromEvent(this.document, 'keyup')
.pipe(filter((event) => event.keyCode === 27), takeUntil(dialogRef.onClose))
.subscribe(() => dialogRef.close());
}
}
}
NbDialogService.decorators = [
{ type: Injectable }
];
NbDialogService.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [NB_DOCUMENT,] }] },
{ type: undefined, decorators: [{ type: Inject, args: [NB_DIALOG_CONFIG,] }] },
{ type: NbPositionBuilderService },
{ type: NbOverlayService },
{ type: Injector },
{ type: ComponentFactoryResolver }
];
//# sourceMappingURL=dialog.service.js.map