From 2edd9b3c232d1ef69ce2eb06c73236db7196632a Mon Sep 17 00:00:00 2001 From: Nikita Poltoratsky Date: Tue, 11 Sep 2018 16:28:01 +0300 Subject: [PATCH] feat(theme): add new Dialog component (#688) Closes #666, Closes #665 --- .../components/{modal.svg => dialog.svg} | 0 docs/structure.ts | 10 + package-lock.json | 9 + scripts/gulp/tasks/bundle/rollup-config.ts | 1 + .../theme/components/cdk/a11y/a11y.module.ts | 12 + .../theme/components/cdk/a11y/focus-trap.ts | 49 ++++ src/framework/theme/components/cdk/index.ts | 6 +- .../theme/components/cdk/overlay/mapping.ts | 22 +- .../theme/components/dialog/dialog-config.ts | 59 +++++ .../components/dialog/dialog-container.ts | 59 +++++ .../theme/components/dialog/dialog-ref.ts | 43 ++++ .../theme/components/dialog/dialog.module.ts | 31 +++ .../components/dialog/dialog.service.spec.ts | 147 ++++++++++++ .../theme/components/dialog/dialog.service.ts | 222 ++++++++++++++++++ .../theme/components/dialog/index.ts | 10 + src/framework/theme/index.ts | 1 + .../theme/styles/global/_components.scss | 5 +- .../dialog/dialog-auto-focus.component.ts | 69 ++++++ .../dialog/dialog-backdrop-click.component.ts | 68 ++++++ src/playground/dialog/dialog-esc.component.ts | 68 ++++++ .../dialog/dialog-has-backdrop.component.ts | 68 ++++++ .../dialog/dialog-result.component.ts | 63 +++++ .../dialog/dialog-scroll.component.ts | 68 ++++++ .../dialog/dialog-showcase.component.ts | 53 +++++ .../dialog/dialog-template.component.ts | 32 +++ src/playground/playground-routing.module.ts | 48 +++- src/playground/playground.module.ts | 37 +++ 27 files changed, 1252 insertions(+), 8 deletions(-) rename docs/assets/images/components/{modal.svg => dialog.svg} (100%) create mode 100644 src/framework/theme/components/cdk/a11y/a11y.module.ts create mode 100644 src/framework/theme/components/cdk/a11y/focus-trap.ts create mode 100644 src/framework/theme/components/dialog/dialog-config.ts create mode 100644 src/framework/theme/components/dialog/dialog-container.ts create mode 100644 src/framework/theme/components/dialog/dialog-ref.ts create mode 100644 src/framework/theme/components/dialog/dialog.module.ts create mode 100644 src/framework/theme/components/dialog/dialog.service.spec.ts create mode 100644 src/framework/theme/components/dialog/dialog.service.ts create mode 100644 src/framework/theme/components/dialog/index.ts create mode 100644 src/playground/dialog/dialog-auto-focus.component.ts create mode 100644 src/playground/dialog/dialog-backdrop-click.component.ts create mode 100644 src/playground/dialog/dialog-esc.component.ts create mode 100644 src/playground/dialog/dialog-has-backdrop.component.ts create mode 100644 src/playground/dialog/dialog-result.component.ts create mode 100644 src/playground/dialog/dialog-scroll.component.ts create mode 100644 src/playground/dialog/dialog-showcase.component.ts create mode 100644 src/playground/dialog/dialog-template.component.ts diff --git a/docs/assets/images/components/modal.svg b/docs/assets/images/components/dialog.svg similarity index 100% rename from docs/assets/images/components/modal.svg rename to docs/assets/images/components/dialog.svg diff --git a/docs/structure.ts b/docs/structure.ts index 2eb9d067ec..2886430443 100644 --- a/docs/structure.ts +++ b/docs/structure.ts @@ -302,6 +302,16 @@ export const structure = [ 'NbContextMenuDirective', ], }, + { + type: 'tabs', + name: 'Dialog', + icon: 'dialog.svg', + source: [ + 'NbDialogService', + 'NbDialogRef', + 'NbDialogConfig', + ], + }, { type: 'group', name: 'Extra', diff --git a/package-lock.json b/package-lock.json index 27dfa7da96..3e65eeba01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -232,6 +232,15 @@ "tslib": "1.9.1" } }, + "@angular/cdk": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.0.0.tgz", + "integrity": "sha512-GVWUwmKWJPkK4gJTi0tgaLDs5QlRvkozIs6KnrsozkPUNDIsZyQCyEUB+llHiUB9AeDGcCDbpQyGIDLdya5khQ==", + "dev": true, + "requires": { + "tslib": "1.9.1" + } + }, "@angular/cli": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.0.3.tgz", diff --git a/scripts/gulp/tasks/bundle/rollup-config.ts b/scripts/gulp/tasks/bundle/rollup-config.ts index 61e786f55c..3815eba4d4 100644 --- a/scripts/gulp/tasks/bundle/rollup-config.ts +++ b/scripts/gulp/tasks/bundle/rollup-config.ts @@ -20,6 +20,7 @@ const ROLLUP_GLOBALS = { '@angular/cdk/overlay': 'ng.cdk.overlay', '@angular/cdk/platform': 'ng.cdk.platform', '@angular/cdk/portal': 'ng.cdk.portal', + '@angular/cdk/a11y': 'ng.cdk.a11y', // RxJS dependencies diff --git a/src/framework/theme/components/cdk/a11y/a11y.module.ts b/src/framework/theme/components/cdk/a11y/a11y.module.ts new file mode 100644 index 0000000000..005ed21819 --- /dev/null +++ b/src/framework/theme/components/cdk/a11y/a11y.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; + +import { NbFocusTrapFactoryService } from './focus-trap'; + + +@NgModule({ + providers: [ + NbFocusTrapFactoryService, + ], +}) +export class NbA11yModule { +} diff --git a/src/framework/theme/components/cdk/a11y/focus-trap.ts b/src/framework/theme/components/cdk/a11y/focus-trap.ts new file mode 100644 index 0000000000..b5b541e12d --- /dev/null +++ b/src/framework/theme/components/cdk/a11y/focus-trap.ts @@ -0,0 +1,49 @@ +import { Inject, Injectable, NgZone } from '@angular/core'; +import { FocusTrap, FocusTrapFactory, InteractivityChecker } from '@angular/cdk/a11y'; + +import { NB_DOCUMENT } from '../../../theme.options'; + + +/** + * Overrides angular cdk focus trap to keep restore functionality inside trap. + * */ +export class NbFocusTrap extends FocusTrap { + protected previouslyFocusedElement: HTMLElement; + + constructor( + protected element: HTMLElement, + protected checker: InteractivityChecker, + protected ngZone: NgZone, + protected document: Document, + deferAnchors) { + super(element, checker, ngZone, document, deferAnchors); + this.savePreviouslyFocusedElement(); + } + + restoreFocus() { + this.previouslyFocusedElement.focus(); + this.destroy(); + } + + blurPreviouslyFocusedElement() { + this.previouslyFocusedElement.blur(); + } + + protected savePreviouslyFocusedElement() { + this.previouslyFocusedElement = this.document.activeElement as HTMLElement; + } +} + +@Injectable() +export class NbFocusTrapFactoryService extends FocusTrapFactory { + constructor( + protected checker: InteractivityChecker, + protected ngZone: NgZone, + @Inject(NB_DOCUMENT) private document) { + super(checker, ngZone, document); + } + + create(element: HTMLElement, deferCaptureElements?: boolean): NbFocusTrap { + return new NbFocusTrap(element, this.checker, this.ngZone, this.document, deferCaptureElements); + } +} diff --git a/src/framework/theme/components/cdk/index.ts b/src/framework/theme/components/cdk/index.ts index 00f4f1c674..3eebe8a106 100644 --- a/src/framework/theme/components/cdk/index.ts +++ b/src/framework/theme/components/cdk/index.ts @@ -1,2 +1,6 @@ -export * from './overlay/mapping'; export * from './overlay'; +export * from './a11y/a11y.module'; +export * from './a11y/focus-trap'; +export * from './adapter/overlay-container-adapter'; +export * from './adapter/scroll-dispatcher-adapter'; +export * from './adapter/viewport-ruler-adapter'; diff --git a/src/framework/theme/components/cdk/overlay/mapping.ts b/src/framework/theme/components/cdk/overlay/mapping.ts index 0b669528ec..8d369a258d 100644 --- a/src/framework/theme/components/cdk/overlay/mapping.ts +++ b/src/framework/theme/components/cdk/overlay/mapping.ts @@ -1,5 +1,13 @@ import { Directive, Injectable, NgModule, TemplateRef, ViewContainerRef } from '@angular/core'; -import { CdkPortal, ComponentPortal, Portal, PortalModule, TemplatePortal } from '@angular/cdk/portal'; +import { + CdkPortal, + CdkPortalOutlet, + ComponentPortal, + Portal, + PortalInjector, + PortalModule, + TemplatePortal, +} from '@angular/cdk/portal'; import { ComponentType, ConnectedOverlayPositionChange, @@ -13,6 +21,7 @@ import { OverlayPositionBuilder, OverlayRef, PositionStrategy, + ScrollStrategy, } from '@angular/cdk/overlay'; import { Platform } from '@angular/cdk/platform'; @@ -21,6 +30,10 @@ import { Platform } from '@angular/cdk/platform'; export class NbPortalDirective extends CdkPortal { } +@Directive({ selector: '[nbPortalOutlet]' }) +export class NbPortalOutletDirective extends CdkPortalOutlet { +} + @Injectable() export class NbOverlayService extends Overlay { } @@ -48,6 +61,9 @@ export class NbOverlayContainer extends OverlayContainer { export class NbFlexibleConnectedPositionStrategy extends FlexibleConnectedPositionStrategy { } +export class NbPortalInjector extends PortalInjector { +} + export type NbPortal = Portal; export type NbOverlayRef = OverlayRef; export type NbComponentType = ComponentType; @@ -56,6 +72,7 @@ export type NbPositionStrategy = PositionStrategy; export type NbConnectedPosition = ConnectedPosition; export type NbConnectedOverlayPositionChange = ConnectedOverlayPositionChange; export type NbConnectionPositionPair = ConnectionPositionPair; +export type NbScrollStrategy = ScrollStrategy; const CDK_MODULES = [OverlayModule, PortalModule]; @@ -74,8 +91,9 @@ const CDK_PROVIDERS = [ exports: [ ...CDK_MODULES, NbPortalDirective, + NbPortalOutletDirective, ], - declarations: [NbPortalDirective], + declarations: [NbPortalDirective, NbPortalOutletDirective], providers: [...CDK_PROVIDERS], }) export class NbCdkMappingModule { diff --git a/src/framework/theme/components/dialog/dialog-config.ts b/src/framework/theme/components/dialog/dialog-config.ts new file mode 100644 index 0000000000..34ad86ccc6 --- /dev/null +++ b/src/framework/theme/components/dialog/dialog-config.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { InjectionToken, ViewContainerRef } from '@angular/core'; + + +export const NB_DIALOG_CONFIG = new InjectionToken('Default dialog options'); + +/** + * Describes all available options that may be passed to the NbDialogService. + * */ +export class NbDialogConfig { + /** + * If true than overlay will render backdrop under a dialog. + * */ + hasBackdrop: boolean = true; + + /** + * Class that'll be assigned to the backdrop element. + * */ + backdropClass: string = 'overlay-backdrop'; + + /** + * If true then mouse clicks by backdrop will close a dialog. + * */ + closeOnBackdropClick: boolean = true; + + /** + * If true then escape press will close a dialog. + * */ + closeOnEsc: boolean = true; + + /** + * Disables scroll on content under dialog if true and does nothing otherwise. + * */ + hasScroll: boolean = false; + + /** + * Focuses dialog automatically after open if true. + * */ + autoFocus: boolean = true; + + /** + * Where the attached component should live in Angular's *logical* component tree. + * This affects what is available for injection and the change detection order for the + * component instantiated inside of the dialog. This does not affect where the dialog + * content will be rendered. + */ + viewContainerRef: ViewContainerRef; + + context: D; + + constructor(config: Partial) { + Object.assign(this, config); + } +} diff --git a/src/framework/theme/components/dialog/dialog-container.ts b/src/framework/theme/components/dialog/dialog-container.ts new file mode 100644 index 0000000000..cb945b73a9 --- /dev/null +++ b/src/framework/theme/components/dialog/dialog-container.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { Component, ComponentRef, ElementRef, EmbeddedViewRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; + +import { + NbComponentPortal, + NbFocusTrap, + NbFocusTrapFactoryService, + NbPortalOutletDirective, + NbTemplatePortal, +} from '../cdk'; +import { NbDialogConfig } from './dialog-config'; + + +/** + * Container component for each dialog. + * All the dialogs will be attached to it. + * // TODO add animations + * */ +@Component({ + selector: 'nb-dialog-container', + template: '', +}) +export class NbDialogContainerComponent implements OnInit, OnDestroy { + @ViewChild(NbPortalOutletDirective) portalOutlet: NbPortalOutletDirective; + + protected focusTrap: NbFocusTrap; + + constructor(protected config: NbDialogConfig, + protected elementRef: ElementRef, + protected focusTrapFactory: NbFocusTrapFactoryService) { + } + + ngOnInit() { + if (this.config.autoFocus) { + this.focusTrap = this.focusTrapFactory.create(this.elementRef.nativeElement); + this.focusTrap.blurPreviouslyFocusedElement(); + this.focusTrap.focusInitialElement(); + } + } + + ngOnDestroy() { + if (this.config.autoFocus && this.focusTrap) { + this.focusTrap.restoreFocus(); + } + } + + attachComponentPortal(portal: NbComponentPortal): ComponentRef { + return this.portalOutlet.attachComponentPortal(portal); + } + + attachTemplatePortal(portal: NbTemplatePortal): EmbeddedViewRef { + return this.portalOutlet.attachTemplatePortal(portal); + } +} diff --git a/src/framework/theme/components/dialog/dialog-ref.ts b/src/framework/theme/components/dialog/dialog-ref.ts new file mode 100644 index 0000000000..4e06e74896 --- /dev/null +++ b/src/framework/theme/components/dialog/dialog-ref.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ComponentRef } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; + +import { NbOverlayRef } from '../cdk'; + + +/** + * The `NbDialogRef` helps to manipulate dialog after it was created. + * The dialog can be dismissed by using `close` method of the dialogRef. + * You can access rendered component as `content` property of the dialogRef. + * `onBackdropClick` streams click events on the backdrop of the dialog. + * */ +export class NbDialogRef { + + componentRef: ComponentRef; + + /** + * Stream of backdrop click events. + * */ + readonly onBackdropClick: Observable; + protected onClose$: Subject = new Subject(); + readonly onClose: Observable = this.onClose$.asObservable(); + + constructor(protected overlayRef: NbOverlayRef) { + this.onBackdropClick = this.overlayRef.backdropClick(); + } + + /** + * Hides dialog. + * */ + close(res?: any) { + this.overlayRef.detach(); + this.overlayRef.dispose(); + this.onClose$.next(res); + this.onClose$.complete(); + } +} diff --git a/src/framework/theme/components/dialog/dialog.module.ts b/src/framework/theme/components/dialog/dialog.module.ts new file mode 100644 index 0000000000..8bbdb157eb --- /dev/null +++ b/src/framework/theme/components/dialog/dialog.module.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ModuleWithProviders, NgModule } from '@angular/core'; + +import { NbSharedModule } from '../shared/shared.module'; +import { NbA11yModule, NbOverlayModule } from '../cdk'; +import { NbDialogService } from './dialog.service'; +import { NbDialogContainerComponent } from './dialog-container'; +import { NB_DIALOG_CONFIG, NbDialogConfig } from './dialog-config'; + + +@NgModule({ + imports: [NbSharedModule, NbA11yModule, NbOverlayModule], + declarations: [NbDialogContainerComponent], + entryComponents: [NbDialogContainerComponent], +}) +export class NbDialogModule { + static forRoot(dialogConfig: Partial = {}): ModuleWithProviders { + return { + ngModule: NbDialogModule, + providers: [ + NbDialogService, + { provide: NB_DIALOG_CONFIG, useValue: dialogConfig }, + ], + } + } +} diff --git a/src/framework/theme/components/dialog/dialog.service.spec.ts b/src/framework/theme/components/dialog/dialog.service.spec.ts new file mode 100644 index 0000000000..c60c7a7fc1 --- /dev/null +++ b/src/framework/theme/components/dialog/dialog.service.spec.ts @@ -0,0 +1,147 @@ +import { Component, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + +import { NbOverlayContainerAdapter, NbOverlayModule, NbOverlayService } from '../cdk'; +import { NbDialogService } from './dialog.service'; +import { NbDialogModule } from './dialog.module'; +import { NbThemeModule } from '../../theme.module'; +import { NB_DOCUMENT } from '../../theme.options'; + + +@Component({ selector: 'nb-test-dialog', template: '' }) +class NbTestDialogComponent { +} + +@NgModule({ + declarations: [NbTestDialogComponent], + entryComponents: [NbTestDialogComponent], +}) +class NbTestDialogModule { +} + + +describe('dialog-service', () => { + let dialog: NbDialogService; + let overlayContainerService: NbOverlayContainerAdapter; + let overlayContainer: HTMLElement; + let overlayService: NbOverlayService; + let document: Document; + + const queryBackdrop = () => overlayContainer.querySelector('.overlay-backdrop'); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + NbTestDialogModule, + NbThemeModule.forRoot({ name: 'default' }), + NbDialogModule.forRoot(), + NbOverlayModule.forRoot(), + ], + }); + + dialog = TestBed.get(NbDialogService); + overlayContainerService = TestBed.get(NbOverlayContainerAdapter); + document = TestBed.get(NB_DOCUMENT); + overlayService = TestBed.get(NbOverlayService); + }); + + beforeEach(() => { + overlayContainer = document.createElement('div'); + overlayContainerService.setContainer(overlayContainer); + }); + + afterAll(() => { + overlayContainerService.clearContainer(); + }); + + it('should render dialog', () => { + const ref = dialog.open(NbTestDialogComponent); + expect(ref.componentRef).toBeTruthy(); + expect(ref.componentRef.instance instanceof NbTestDialogComponent).toBeTruthy(); + }); + + it('should assign default backdropClass', () => { + dialog.open(NbTestDialogComponent); + expect(queryBackdrop()).toBeTruthy(); + }); + + it('should assign backdropClass if provided', () => { + dialog.open(NbTestDialogComponent, { backdropClass: 'nb-overlay-test-backdrop-class' }); + expect(overlayContainer.querySelector('.nb-overlay-test-backdrop-class')).toBeTruthy(); + }); + + + it('should render with backdrop if hasBackdrop is true', () => { + dialog.open(NbTestDialogComponent, { hasBackdrop: true }); + expect(queryBackdrop()).toBeTruthy(); + }); + + it('should render without backdrop if hasBackdrop is false', () => { + dialog.open(NbTestDialogComponent, { hasBackdrop: false }); + expect(queryBackdrop()).toBeFalsy(); + }); + + it('should leave capability scroll content under dialog if hasScroll is true', () => { + const noopSpy = spyOn(overlayService.scrollStrategies, 'noop'); + const blockSpy = spyOn(overlayService.scrollStrategies, 'block'); + + dialog.open(NbTestDialogComponent, { hasScroll: true }); + + expect(noopSpy).toHaveBeenCalledTimes(1); + expect(blockSpy).toHaveBeenCalledTimes(0); + }); + + it('should disable scroll under dialog if hasScroll is false', () => { + const noopSpy = spyOn(overlayService.scrollStrategies, 'noop'); + const blockSpy = spyOn(overlayService.scrollStrategies, 'block'); + + dialog.open(NbTestDialogComponent, { hasScroll: false }); + + expect(noopSpy).toHaveBeenCalledTimes(0); + expect(blockSpy).toHaveBeenCalledTimes(1); + }); + + it('should fire onBackdropClick if backdrop was clicked', done => { + const ref = dialog.open(NbTestDialogComponent, { closeOnBackdropClick: false }); + const backdrop = queryBackdrop(); + + ref.onBackdropClick.subscribe(e => { + expect(e.target).toBe(backdrop); + done(); + }); + + backdrop.dispatchEvent(new Event('click')); + }); + + it('should not fire onBackdropClick if backdrop wasn\'t clicked', () => { + const ref = dialog.open(NbTestDialogComponent, { closeOnBackdropClick: false }); + const spy = jasmine.createSpy(); + ref.onBackdropClick.subscribe(spy); + expect(spy).toHaveBeenCalledTimes(0); + }); + + it('should close on backdrop click if closeOnBackdropClick is true', () => { + dialog.open(NbTestDialogComponent, { closeOnBackdropClick: true, autoFocus: false }); + queryBackdrop().dispatchEvent(new Event('click')); + expect(queryBackdrop()).toBeFalsy(); + }); + + it('should not close on backdrop click if closeOnBackdropClick is false', () => { + dialog.open(NbTestDialogComponent, { closeOnBackdropClick: false }); + queryBackdrop().dispatchEvent(new Event('click')); + expect(queryBackdrop()).toBeTruthy(); + }); + + it('should close on escape press if closeOnEsc is true', () => { + dialog.open(NbTestDialogComponent, { closeOnEsc: true }); + document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 27 })); + expect(queryBackdrop()).toBeFalsy(); + }); + + it('should not close on escape press if closeOnEsc is false', () => { + dialog.open(NbTestDialogComponent, { closeOnEsc: false }); + document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 27 })); + expect(queryBackdrop()).toBeTruthy(); + }); +}); + diff --git a/src/framework/theme/components/dialog/dialog.service.ts b/src/framework/theme/components/dialog/dialog.service.ts new file mode 100644 index 0000000000..5a28190e44 --- /dev/null +++ b/src/framework/theme/components/dialog/dialog.service.ts @@ -0,0 +1,222 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { Inject, Injectable, Injector, TemplateRef, Type } from '@angular/core'; +import { fromEvent as observableFromEvent } from 'rxjs'; +import { filter } from 'rxjs/operators'; + +import { + NbComponentPortal, + NbGlobalPositionStrategy, + NbOverlayRef, + NbOverlayService, + NbPortalInjector, + NbPositionBuilderService, + NbScrollStrategy, + NbTemplatePortal, +} from '../cdk'; +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. + * + * ```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 + * + * {{ some-additional-data }} + * + * ``` + * + * `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) + * */ +@Injectable() +export class NbDialogService { + constructor(@Inject(NB_DOCUMENT) protected document, + @Inject(NB_DIALOG_CONFIG) protected globalConfig, + protected positionBuilder: NbPositionBuilderService, + protected overlay: NbOverlayService, + protected injector: Injector) { + } + + /** + * Opens new instance of the dialog, may receive optional config. + * */ + open(content: Type | TemplateRef, userConfig: Partial> = {}): NbDialogRef { + const config = new NbDialogConfig({ ...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; + } + + protected createOverlay(config: NbDialogConfig): NbOverlayRef { + const positionStrategy = this.createPositionStrategy(); + const scrollStrategy = this.createScrollStrategy(config.hasScroll); + + return this.overlay.create({ + positionStrategy, + scrollStrategy, + hasBackdrop: config.hasBackdrop, + backdropClass: config.backdropClass, + }); + } + + protected createPositionStrategy(): NbGlobalPositionStrategy { + return this.positionBuilder + .global() + .centerVertically() + .centerHorizontally(); + } + + protected createScrollStrategy(hasScroll: boolean): NbScrollStrategy { + if (hasScroll) { + return this.overlay.scrollStrategies.noop(); + } else { + return this.overlay.scrollStrategies.block(); + } + } + + protected createContainer(config: NbDialogConfig, overlayRef: NbOverlayRef): NbDialogContainerComponent { + const injector = new NbPortalInjector(this.createInjector(config), new WeakMap([[NbDialogConfig, config]])); + const containerPortal = new NbComponentPortal(NbDialogContainerComponent, null, injector); + const containerRef = overlayRef.attach(containerPortal); + return containerRef.instance; + } + + protected createContent(config: NbDialogConfig, + content: Type | TemplateRef, + container: NbDialogContainerComponent, + dialogRef: NbDialogRef) { + 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, { ...config.context }) + } + } + } + + protected createTemplatePortal(config: NbDialogConfig, + content: TemplateRef, + dialogRef: NbDialogRef): NbTemplatePortal { + 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. + * */ + protected createComponentPortal(config: NbDialogConfig, + content: Type, + dialogRef: NbDialogRef): NbComponentPortal { + const injector = this.createInjector(config); + const portalInjector = new NbPortalInjector(injector, new WeakMap([[NbDialogRef, dialogRef]])); + return new NbComponentPortal(content, config.viewContainerRef, portalInjector); + } + + protected createInjector(config: NbDialogConfig): Injector { + return config.viewContainerRef && config.viewContainerRef.injector || this.injector; + } + + protected registerCloseListeners(config: NbDialogConfig, overlayRef: NbOverlayRef, dialogRef: NbDialogRef) { + if (config.closeOnBackdropClick) { + overlayRef.backdropClick().subscribe(() => dialogRef.close()); + } + + if (config.closeOnEsc) { + observableFromEvent(this.document, 'keyup') + .pipe(filter((event: KeyboardEvent) => event.keyCode === 27)) + .subscribe(() => dialogRef.close()); + } + } +} diff --git a/src/framework/theme/components/dialog/index.ts b/src/framework/theme/components/dialog/index.ts new file mode 100644 index 0000000000..70a4c20a75 --- /dev/null +++ b/src/framework/theme/components/dialog/index.ts @@ -0,0 +1,10 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +export * from './dialog-config'; +export * from './dialog-ref'; +export * from './dialog.service'; +export * from './dialog.module'; diff --git a/src/framework/theme/index.ts b/src/framework/theme/index.ts index 6bfba856fd..01cfdacd9b 100644 --- a/src/framework/theme/index.ts +++ b/src/framework/theme/index.ts @@ -71,3 +71,4 @@ export * from './components/list/infinite-list.directive'; export * from './components/input/input.directive'; export * from './components/input/input.module'; export * from './components/cdk/overlay'; +export * from './components/dialog'; diff --git a/src/framework/theme/styles/global/_components.scss b/src/framework/theme/styles/global/_components.scss index a0c0597337..d222f74262 100644 --- a/src/framework/theme/styles/global/_components.scss +++ b/src/framework/theme/styles/global/_components.scss @@ -4,6 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ +@import '~@angular/cdk/overlay-prebuilt.css'; @import '../../components/layout/layout.component.theme'; @import '../../components/sidebar/sidebar.component.theme'; @import '../../components/calendar-kit/calendar-kit.theme'; @@ -20,8 +21,6 @@ @import '../../components/checkbox/checkbox.component.theme'; @import '../../components/progress-bar/progress-bar.component.theme'; @import '../../components/badge/badge.component.theme'; -@import '../../components/popover/popover.component.theme'; -@import '../../components/context-menu/context-menu.component.theme'; @import '../../components/alert/alert.component.theme'; @import '../../components/chat/chat.component.theme'; @import '../../components/spinner/spinner.component.theme'; @@ -53,9 +52,7 @@ @include nb-checkbox-theme(); @include nb-progress-bar-theme(); @include nb-badge-theme(); - @include nb-popover-theme(); @include nb-stepper-theme(); - @include nb-context-menu-theme(); @include nb-alert-theme(); @include nb-chat-theme(); @include nb-accordion-theme(); diff --git a/src/playground/dialog/dialog-auto-focus.component.ts b/src/playground/dialog/dialog-auto-focus.component.ts new file mode 100644 index 0000000000..7bdf32e3a1 --- /dev/null +++ b/src/playground/dialog/dialog-auto-focus.component.ts @@ -0,0 +1,69 @@ +import { Component, Input } from '@angular/core'; + +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog', + template: ` + + {{ title }} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis tincidunt tincidunt. + Vestibulum vulputate maximus massa vel tristique. Suspendisse potenti. Duis aliquet purus sed dictum dictum. + Donec fringilla, purus at fermentum imperdiet, velit enim malesuada turpis, quis luctus arcu arcu nec orci. + Duis eu mattis felis. Quisque sollicitudin elementum nunc vel tincidunt. Vestibulum egestas mi nec + iaculis varius. Morbi in risus sed sapien ultricies feugiat. Quisque pulvinar mattis purus, + in aliquet massa aliquet et. + + + + + + `, +}) +export class NbAutoFocusDialogComponent { + @Input() title: string; + + constructor(protected ref: NbDialogRef) { + } + + dismiss() { + this.ref.close(); + } +} + +@Component({ + selector: 'nb-dialog-auto-focus', + template: ` +
+ + +
+ `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + + button { + margin: 1rem; + } + `], +}) +export class NbDialogAutoFocusComponent { + constructor(private dialogService: NbDialogService) { + } + + openWithAutoFocus() { + this.open(true); + } + + openWithoutAutoFocus() { + this.open(false); + } + + protected open(autoFocus: boolean) { + this.dialogService.open(NbAutoFocusDialogComponent, { autoFocus }); + } +} diff --git a/src/playground/dialog/dialog-backdrop-click.component.ts b/src/playground/dialog/dialog-backdrop-click.component.ts new file mode 100644 index 0000000000..a19a161543 --- /dev/null +++ b/src/playground/dialog/dialog-backdrop-click.component.ts @@ -0,0 +1,68 @@ +import { Component, Input } from '@angular/core'; +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog', + template: ` + + {{ title }} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis tincidunt tincidunt. + Vestibulum vulputate maximus massa vel tristique. Suspendisse potenti. Duis aliquet purus sed dictum dictum. + Donec fringilla, purus at fermentum imperdiet, velit enim malesuada turpis, quis luctus arcu arcu nec orci. + Duis eu mattis felis. Quisque sollicitudin elementum nunc vel tincidunt. Vestibulum egestas mi nec + iaculis varius. Morbi in risus sed sapien ultricies feugiat. Quisque pulvinar mattis purus, + in aliquet massa aliquet et. + + + + + + `, +}) +export class NbBackdropClickDialogComponent { + @Input() title: string; + + constructor(protected ref: NbDialogRef) { + } + + dismiss() { + this.ref.close(); + } +} + +@Component({ + selector: 'nb-dialog-backdrop-click', + template: ` +
+ + +
+ `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + + button { + margin: 1rem; + } + `], +}) +export class NbDialogBackdropClickComponent { + constructor(private dialogService: NbDialogService) { + } + + openWithBackdropClick() { + this.open(true); + } + + openWithoutBackdropClick() { + this.open(false); + } + + protected open(closeOnBackdropClick: boolean) { + this.dialogService.open(NbBackdropClickDialogComponent, { closeOnBackdropClick }); + } +} diff --git a/src/playground/dialog/dialog-esc.component.ts b/src/playground/dialog/dialog-esc.component.ts new file mode 100644 index 0000000000..00891adf06 --- /dev/null +++ b/src/playground/dialog/dialog-esc.component.ts @@ -0,0 +1,68 @@ +import { Component, Input } from '@angular/core'; +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog', + template: ` + + {{ title }} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis tincidunt tincidunt. + Vestibulum vulputate maximus massa vel tristique. Suspendisse potenti. Duis aliquet purus sed dictum dictum. + Donec fringilla, purus at fermentum imperdiet, velit enim malesuada turpis, quis luctus arcu arcu nec orci. + Duis eu mattis felis. Quisque sollicitudin elementum nunc vel tincidunt. Vestibulum egestas mi nec + iaculis varius. Morbi in risus sed sapien ultricies feugiat. Quisque pulvinar mattis purus, + in aliquet massa aliquet et. + + + + + + `, +}) +export class NbEscDialogComponent { + @Input() title: string; + + constructor(protected ref: NbDialogRef) { + } + + dismiss() { + this.ref.close(); + } +} + +@Component({ + selector: 'nb-dialog-esc', + template: ` +
+ + +
+ `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + + button { + margin: 1rem; + } + `], +}) +export class NbDialogEscComponent { + constructor(private dialogService: NbDialogService) { + } + + openWithEscClose() { + this.open(true); + } + + openWithoutEscClose() { + this.open(false); + } + + protected open(closeOnEsc: boolean) { + this.dialogService.open(NbEscDialogComponent, { closeOnEsc }); + } +} diff --git a/src/playground/dialog/dialog-has-backdrop.component.ts b/src/playground/dialog/dialog-has-backdrop.component.ts new file mode 100644 index 0000000000..6297649f8a --- /dev/null +++ b/src/playground/dialog/dialog-has-backdrop.component.ts @@ -0,0 +1,68 @@ +import { Component, Input } from '@angular/core'; +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog', + template: ` + + {{ title }} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis tincidunt tincidunt. + Vestibulum vulputate maximus massa vel tristique. Suspendisse potenti. Duis aliquet purus sed dictum dictum. + Donec fringilla, purus at fermentum imperdiet, velit enim malesuada turpis, quis luctus arcu arcu nec orci. + Duis eu mattis felis. Quisque sollicitudin elementum nunc vel tincidunt. Vestibulum egestas mi nec + iaculis varius. Morbi in risus sed sapien ultricies feugiat. Quisque pulvinar mattis purus, + in aliquet massa aliquet et. + + + + + + `, +}) +export class NbHasBackdropDialogComponent { + @Input() title: string; + + constructor(protected ref: NbDialogRef) { + } + + dismiss() { + this.ref.close(); + } +} + +@Component({ + selector: 'nb-dialog-has-backdrop', + template: ` +
+ + +
+ `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + + button { + margin: 1rem; + } + `], +}) +export class NbDialogHasBackdropComponent { + constructor(private dialogService: NbDialogService) { + } + + openWithBackdrop() { + this.open(true); + } + + openWithoutBackdrop() { + this.open(false); + } + + protected open(hasBackdrop: boolean) { + this.dialogService.open(NbHasBackdropDialogComponent, { hasBackdrop }); + } +} diff --git a/src/playground/dialog/dialog-result.component.ts b/src/playground/dialog/dialog-result.component.ts new file mode 100644 index 0000000000..111209642a --- /dev/null +++ b/src/playground/dialog/dialog-result.component.ts @@ -0,0 +1,63 @@ +import { Component } from '@angular/core'; +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + +@Component({ + selector: 'nb-name-prompt', + template: ` + + Enter your name + + + + + + + + + `, + styles: [` + button { + margin: 1rem; + } + `], +}) +export class NbDialogNamePromptComponent { + constructor(protected dialogRef: NbDialogRef) { + } + + cancel() { + this.dialogRef.close(); + } + + submit(name) { + this.dialogRef.close(name); + } +} + +@Component({ + selector: 'nb-dialog-result', + template: ` + +
+

Names:

+
    +
  • {{ name }}
  • +
+ `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + `], +}) +export class NbDialogResultComponent { + names: string[] = []; + + constructor(private dialogService: NbDialogService) { + } + + open() { + this.dialogService.open(NbDialogNamePromptComponent) + .onClose.subscribe(name => name && this.names.push(name)); + } +} diff --git a/src/playground/dialog/dialog-scroll.component.ts b/src/playground/dialog/dialog-scroll.component.ts new file mode 100644 index 0000000000..c4176cbd5f --- /dev/null +++ b/src/playground/dialog/dialog-scroll.component.ts @@ -0,0 +1,68 @@ +import { Component, Input } from '@angular/core'; +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog', + template: ` + + {{ title }} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis tincidunt tincidunt. + Vestibulum vulputate maximus massa vel tristique. Suspendisse potenti. Duis aliquet purus sed dictum dictum. + Donec fringilla, purus at fermentum imperdiet, velit enim malesuada turpis, quis luctus arcu arcu nec orci. + Duis eu mattis felis. Quisque sollicitudin elementum nunc vel tincidunt. Vestibulum egestas mi nec + iaculis varius. Morbi in risus sed sapien ultricies feugiat. Quisque pulvinar mattis purus, + in aliquet massa aliquet et. + + + + + + `, +}) +export class NbScrollDialogComponent { + @Input() title: string; + + constructor(protected ref: NbDialogRef) { + } + + dismiss() { + this.ref.close(); + } +} + +@Component({ + selector: 'nb-dialog-scroll', + template: ` +
+ + +
+ `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + + button { + margin: 1rem; + } + `], +}) +export class NbDialogScrollComponent { + constructor(private dialogService: NbDialogService) { + } + + openWithScroll() { + this.open(true); + } + + openWithoutScroll() { + this.open(false); + } + + protected open(hasScroll: boolean) { + this.dialogService.open(NbScrollDialogComponent, { hasScroll }); + } +} diff --git a/src/playground/dialog/dialog-showcase.component.ts b/src/playground/dialog/dialog-showcase.component.ts new file mode 100644 index 0000000000..4ea34b9500 --- /dev/null +++ b/src/playground/dialog/dialog-showcase.component.ts @@ -0,0 +1,53 @@ +import { Component, Input } from '@angular/core'; +import { NbDialogRef, NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog', + template: ` + + {{ title }} + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis tincidunt tincidunt. + Vestibulum vulputate maximus massa vel tristique. Suspendisse potenti. Duis aliquet purus sed dictum dictum. + Donec fringilla, purus at fermentum imperdiet, velit enim malesuada turpis, quis luctus arcu arcu nec orci. + Duis eu mattis felis. Quisque sollicitudin elementum nunc vel tincidunt. Vestibulum egestas mi nec + iaculis varius. Morbi in risus sed sapien ultricies feugiat. Quisque pulvinar mattis purus, + in aliquet massa aliquet et. + + + + + + `, +}) +export class NbShowcaseDialogComponent { + @Input() title: string; + + constructor(protected ref: NbDialogRef) { + } + + dismiss() { + this.ref.close(); + } +} + +@Component({ + selector: 'nb-dialog-showcase', + template: '', + styles: [` /deep/ nb-layout-column { + height: 80vw; + } `], +}) +export class NbDialogShowcaseComponent { + constructor(private dialogService: NbDialogService) { + } + + open() { + this.dialogService.open(NbShowcaseDialogComponent, { + context: { + title: 'This is a title passed to the dialog component', + }, + }); + } +} diff --git a/src/playground/dialog/dialog-template.component.ts b/src/playground/dialog/dialog-template.component.ts new file mode 100644 index 0000000000..c96d6a9a4e --- /dev/null +++ b/src/playground/dialog/dialog-template.component.ts @@ -0,0 +1,32 @@ +import { Component, TemplateRef } from '@angular/core'; +import { NbDialogService } from '@nebular/theme'; + + +@Component({ + selector: 'nb-dialog-template', + template: ` + + + Template Dialog + {{ data }} + + + + + + + `, + styles: [` + /deep/ nb-layout-column { + height: 80vw; + } + `], +}) +export class NbDialogTemplateComponent { + constructor(private dialogService: NbDialogService) { + } + + open(dialog: TemplateRef) { + this.dialogService.open(dialog, { context: 'this is some additional data passed to dialog' }); + } +} diff --git a/src/playground/playground-routing.module.ts b/src/playground/playground-routing.module.ts index 2cccef9e0d..954d2a06ba 100644 --- a/src/playground/playground-routing.module.ts +++ b/src/playground/playground-routing.module.ts @@ -82,7 +82,8 @@ import { } from './menu/menu-test.component'; import { NbPopoverTestComponent } from './popover/popover-test.component'; import { - NbRouteTabsetShowcaseChild1Component, NbRouteTabsetShowcaseChild2Component, + NbRouteTabsetShowcaseChild1Component, + NbRouteTabsetShowcaseChild2Component, NbRouteTabsetShowcaseComponent, } from './tabset/route-tabset-showcase.component'; import { NbSearchTestComponent } from './search/search-test.component'; @@ -159,6 +160,14 @@ import { NbCalendarMinMaxComponent } from './calendar/calendar-min-max.component import { NbCalendarSizeComponent } from './calendar/calendar-size.component'; import { NbCalendarKitFullCalendarShowcaseComponent } from './calendar-kit/calendar-kit-full-calendar.component'; import { NbOverlayShowcaseComponent } from './overlay/overlay-showcase.component'; +import { NbDialogShowcaseComponent } from './dialog/dialog-showcase.component'; +import { NbDialogHasBackdropComponent } from './dialog/dialog-has-backdrop.component'; +import { NbDialogBackdropClickComponent } from './dialog/dialog-backdrop-click.component'; +import { NbDialogEscComponent } from './dialog/dialog-esc.component'; +import { NbDialogScrollComponent } from './dialog/dialog-scroll.component'; +import { NbDialogAutoFocusComponent } from './dialog/dialog-auto-focus.component'; +import { NbDialogResultComponent } from './dialog/dialog-result.component'; +import { NbDialogTemplateComponent } from './dialog/dialog-template.component'; export const routes: Routes = [ @@ -720,6 +729,43 @@ export const routes: Routes = [ }, ], }, + { + path: 'dialog', + children: [ + { + path: 'dialog-showcase.component', + component: NbDialogShowcaseComponent, + }, + { + path: 'dialog-has-backdrop.component', + component: NbDialogHasBackdropComponent, + }, + { + path: 'dialog-backdrop-click.component', + component: NbDialogBackdropClickComponent, + }, + { + path: 'dialog-esc.component', + component: NbDialogEscComponent, + }, + { + path: 'dialog-scroll.component', + component: NbDialogScrollComponent, + }, + { + path: 'dialog-auto-focus.component', + component: NbDialogAutoFocusComponent, + }, + { + path: 'dialog-result.component', + component: NbDialogResultComponent, + }, + { + path: 'dialog-template.component', + component: NbDialogTemplateComponent, + }, + ], + }, ], }, { diff --git a/src/playground/playground.module.ts b/src/playground/playground.module.ts index ab2e5731b7..ae8ea24b80 100644 --- a/src/playground/playground.module.ts +++ b/src/playground/playground.module.ts @@ -36,6 +36,7 @@ import { NbButtonModule, NbInputModule, NbOverlayModule, + NbDialogModule, } from '@nebular/theme'; import { NbPlaygroundRoutingModule } from './playground-routing.module'; @@ -200,6 +201,17 @@ import { NbCalendarKitMonthCellComponent, } from './calendar-kit/calendar-kit-full-calendar.component'; import { NbOverlayShowcaseComponent } from './overlay/overlay-showcase.component'; +import { NbDialogShowcaseComponent, NbShowcaseDialogComponent } from './dialog/dialog-showcase.component'; +import { NbDialogHasBackdropComponent, NbHasBackdropDialogComponent } from './dialog/dialog-has-backdrop.component'; +import { + NbBackdropClickDialogComponent, + NbDialogBackdropClickComponent, +} from './dialog/dialog-backdrop-click.component'; +import { NbDialogEscComponent, NbEscDialogComponent } from './dialog/dialog-esc.component'; +import { NbDialogScrollComponent, NbScrollDialogComponent } from './dialog/dialog-scroll.component'; +import { NbAutoFocusDialogComponent, NbDialogAutoFocusComponent } from './dialog/dialog-auto-focus.component'; +import { NbDialogNamePromptComponent, NbDialogResultComponent } from './dialog/dialog-result.component'; +import { NbDialogTemplateComponent } from './dialog/dialog-template.component'; export const NB_MODULES = [ NbCardModule, @@ -234,6 +246,7 @@ export const NB_MODULES = [ NbCalendarRangeModule, NbCalendarKitModule, NbOverlayModule.forRoot(), + NbDialogModule.forRoot(), ]; export const NB_EXAMPLE_COMPONENTS = [ @@ -387,6 +400,21 @@ export const NB_EXAMPLE_COMPONENTS = [ NbCalendarKitFullCalendarShowcaseComponent, NbCalendarKitMonthCellComponent, NbOverlayShowcaseComponent, + NbAutoFocusDialogComponent, + NbBackdropClickDialogComponent, + NbEscDialogComponent, + NbHasBackdropDialogComponent, + NbScrollDialogComponent, + NbShowcaseDialogComponent, + NbDialogShowcaseComponent, + NbDialogHasBackdropComponent, + NbDialogBackdropClickComponent, + NbDialogEscComponent, + NbDialogScrollComponent, + NbDialogAutoFocusComponent, + NbDialogResultComponent, + NbDialogNamePromptComponent, + NbDialogTemplateComponent, ]; @NgModule({ @@ -402,6 +430,15 @@ export const NB_EXAMPLE_COMPONENTS = [ NbPlaygroundBaseComponent, ...NB_EXAMPLE_COMPONENTS, ], + entryComponents: [ + NbAutoFocusDialogComponent, + NbDialogNamePromptComponent, + NbEscDialogComponent, + NbHasBackdropDialogComponent, + NbScrollDialogComponent, + NbShowcaseDialogComponent, + NbBackdropClickDialogComponent, + ], }) export class NbPlaygroundModule { }