Skip to content

Commit

Permalink
feat(theme): add new Dialog component (#688)
Browse files Browse the repository at this point in the history
Closes #666, Closes #665
  • Loading branch information
tibing-old-email authored and nnixaa committed Sep 11, 2018
1 parent d3ba6ab commit 2edd9b3
Show file tree
Hide file tree
Showing 27 changed files with 1,252 additions and 8 deletions.
File renamed without changes
10 changes: 10 additions & 0 deletions docs/structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@ export const structure = [
'NbContextMenuDirective',
],
},
{
type: 'tabs',
name: 'Dialog',
icon: 'dialog.svg',
source: [
'NbDialogService',
'NbDialogRef',
'NbDialogConfig',
],
},
{
type: 'group',
name: 'Extra',
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/gulp/tasks/bundle/rollup-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions src/framework/theme/components/cdk/a11y/a11y.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';

import { NbFocusTrapFactoryService } from './focus-trap';


@NgModule({
providers: [
NbFocusTrapFactoryService,
],
})
export class NbA11yModule {
}
49 changes: 49 additions & 0 deletions src/framework/theme/components/cdk/a11y/focus-trap.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
6 changes: 5 additions & 1 deletion src/framework/theme/components/cdk/index.ts
Original file line number Diff line number Diff line change
@@ -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';
22 changes: 20 additions & 2 deletions src/framework/theme/components/cdk/overlay/mapping.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -13,6 +21,7 @@ import {
OverlayPositionBuilder,
OverlayRef,
PositionStrategy,
ScrollStrategy,
} from '@angular/cdk/overlay';
import { Platform } from '@angular/cdk/platform';

Expand All @@ -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 {
}
Expand Down Expand Up @@ -48,6 +61,9 @@ export class NbOverlayContainer extends OverlayContainer {
export class NbFlexibleConnectedPositionStrategy extends FlexibleConnectedPositionStrategy {
}

export class NbPortalInjector extends PortalInjector {
}

export type NbPortal<T = any> = Portal<T>;
export type NbOverlayRef = OverlayRef;
export type NbComponentType<T = any> = ComponentType<T>;
Expand All @@ -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];

Expand All @@ -74,8 +91,9 @@ const CDK_PROVIDERS = [
exports: [
...CDK_MODULES,
NbPortalDirective,
NbPortalOutletDirective,
],
declarations: [NbPortalDirective],
declarations: [NbPortalDirective, NbPortalOutletDirective],
providers: [...CDK_PROVIDERS],
})
export class NbCdkMappingModule {
Expand Down
59 changes: 59 additions & 0 deletions src/framework/theme/components/dialog/dialog-config.ts
Original file line number Diff line number Diff line change
@@ -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<NbDialogConfig>('Default dialog options');

/**
* Describes all available options that may be passed to the NbDialogService.
* */
export class NbDialogConfig<D = any> {
/**
* 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<NbDialogConfig>) {
Object.assign(this, config);
}
}
59 changes: 59 additions & 0 deletions src/framework/theme/components/dialog/dialog-container.ts
Original file line number Diff line number Diff line change
@@ -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: '<ng-template nbPortalOutlet></ng-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<T>(portal: NbComponentPortal<T>): ComponentRef<T> {
return this.portalOutlet.attachComponentPortal(portal);
}

attachTemplatePortal<C>(portal: NbTemplatePortal<C>): EmbeddedViewRef<C> {
return this.portalOutlet.attachTemplatePortal(portal);
}
}
43 changes: 43 additions & 0 deletions src/framework/theme/components/dialog/dialog-ref.ts
Original file line number Diff line number Diff line change
@@ -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<T> {

componentRef: ComponentRef<T>;

/**
* Stream of backdrop click events.
* */
readonly onBackdropClick: Observable<MouseEvent>;
protected onClose$: Subject<any> = new Subject();
readonly onClose: Observable<any> = 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();
}
}
31 changes: 31 additions & 0 deletions src/framework/theme/components/dialog/dialog.module.ts
Original file line number Diff line number Diff line change
@@ -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<NbDialogConfig> = {}): ModuleWithProviders {
return {
ngModule: NbDialogModule,
providers: [
NbDialogService,
{ provide: NB_DIALOG_CONFIG, useValue: dialogConfig },
],
}
}
}
Loading

0 comments on commit 2edd9b3

Please sign in to comment.