Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/dialog #688

Merged
merged 41 commits into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
930a8f6
feat(theme): add cdk
Tibing Aug 27, 2018
1fdbaca
fix(overlay): move mapping in overlay, add overlay showcase
Tibing Aug 27, 2018
be55c10
fix(build): add angular cdk in rollup deps
Tibing Aug 27, 2018
7c770d6
refactor(theme): use overlays in popover, context-menu and search
Tibing Aug 27, 2018
cab0c7a
refactor(overlay): layout sets itself as overlay container
Tibing Aug 27, 2018
8c9db82
refactor(context-menu): move input setter before constructor
Tibing Aug 28, 2018
6a36e34
feat(theme): add modal component
Tibing Aug 28, 2018
9797122
fix(modal): add scroll strategy
Tibing Aug 28, 2018
4e690de
fix(build): add angular cdk in rollup config
Tibing Aug 28, 2018
d42421b
fix(layout): add overlay container as optional dep
Tibing Aug 28, 2018
60d64b7
feat(modal): add docs on NbModalConfig
Tibing Aug 28, 2018
55502ef
feat(cdk): add focus trap wrapper
Tibing Aug 28, 2018
3ba9b1c
feat(modal): docs
Tibing Aug 28, 2018
1a2ae45
feat(modal): setup spec
Tibing Aug 28, 2018
5660ced
fix(popover): e2e
Tibing Aug 28, 2018
b6391a0
fix(context-menu): e2e
Tibing Aug 28, 2018
9f27198
feat(modal): provides capability to inject NbModalRef in modal component
Tibing Aug 29, 2018
1539752
refactor(modal): move modal-ref and modal-config in separate files
Tibing Aug 29, 2018
37e4b7f
refactor(modal): add capability return value
Tibing Aug 29, 2018
8604068
feat(modal): add modal-result example
Tibing Aug 29, 2018
ceaf97d
refactor(modal): rename component to dialog
Tibing Aug 29, 2018
17ac49e
fix(layout): make overlayContainer not optional dep
Tibing Aug 29, 2018
51769c5
feat(overlay): trigger renderer todo
Tibing Aug 30, 2018
13eb427
fix(search): clear search term after close
yggg Aug 30, 2018
d0e0db2
feat(dialog): add capability render template
Tibing Aug 30, 2018
1f1fdfa
fix(dialog): remove redundant imports from dialogref
Tibing Aug 30, 2018
e17f5c2
feat(dialog): template rendering in docs
Tibing Aug 30, 2018
fc735f7
Merge branch 'refactor/overlay-components' into feature/dialog
Tibing Aug 30, 2018
23abe03
test(search): wait for animations completing
yggg Aug 30, 2018
ef71d3b
feat(dialog): specs
Tibing Aug 30, 2018
b8173b1
fix(dialog): docs typos
Tibing Aug 30, 2018
f2cb091
fix(popover): examples
Tibing Aug 30, 2018
67f690c
feat(dialog): add capability inject defaults globally
Tibing Aug 30, 2018
227b9e5
fix(dialog): tests
Tibing Aug 30, 2018
57a41e6
feat(dialog): licenses
Tibing Sep 3, 2018
ad38290
fix(build): add cdk.a11y, remove Document type
Tibing Sep 4, 2018
2e630ca
fix(dialog): spec
Tibing Sep 4, 2018
1dea3b8
Merge branch 'refactor/overlay-components' into feature/dialog
Tibing Sep 7, 2018
38256bb
Merge branch 'master' into feature/dialog
Tibing Sep 11, 2018
7a4efa7
fix(dialog): typos
Tibing Sep 11, 2018
635cc78
fix(dialog): playground
Tibing Sep 11, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
nnixaa marked this conversation as resolved.
Show resolved Hide resolved
'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',
nnixaa marked this conversation as resolved.
Show resolved Hide resolved


// 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> {
Tibing marked this conversation as resolved.
Show resolved Hide resolved
Tibing marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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
nnixaa marked this conversation as resolved.
Show resolved Hide resolved
* */
@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);
Tibing marked this conversation as resolved.
Show resolved Hide resolved
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