Skip to content

Commit

Permalink
feat(theme): add cdk (#679)
Browse files Browse the repository at this point in the history
Add Nebular cdk module, which is a wrapper over Angular cdk and provides some Nebular specific features, such as overrides of the overlay module.

Closes #661, closes #660.
  • Loading branch information
tibing-old-email authored and nnixaa committed Sep 10, 2018
1 parent 22e39eb commit b9283d3
Show file tree
Hide file tree
Showing 23 changed files with 875 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"@angular/cli": "^6.0.0",
"@angular/compiler-cli": "6.0.0",
"@angular/language-service": "6.0.0",
"@angular/cdk": "6.0.0",
"@types/gulp": "3.8.36",
"@types/highlight.js": "9.12.2",
"@types/jasmine": "2.8.3",
Expand Down
3 changes: 3 additions & 0 deletions scripts/gulp/tasks/bundle/rollup-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const ROLLUP_GLOBALS = {
'@angular/core/testing': 'ng.core.testing',
'@angular/common/testing': 'ng.common.testing',
'@angular/common/http/testing': 'ng.common.http.testing',
'@angular/cdk/overlay': 'ng.cdk.overlay',
'@angular/cdk/platform': 'ng.cdk.platform',
'@angular/cdk/portal': 'ng.cdk.portal',


// RxJS dependencies
Expand Down
17 changes: 17 additions & 0 deletions src/framework/theme/components/cdk/adapter/adapter.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { OverlayContainer, ScrollDispatcher } from '@angular/cdk/overlay';

import { NbOverlayContainerAdapter } from './overlay-container-adapter';
import { NbScrollDispatcherAdapter } from './scroll-dispatcher-adapter';
import { NbViewportRulerAdapter } from './viewport-ruler-adapter';


@NgModule({
providers: [
NbViewportRulerAdapter,
{ provide: OverlayContainer, useClass: NbOverlayContainerAdapter },
{ provide: ScrollDispatcher, useClass: NbScrollDispatcherAdapter },
],
})
export class NbCdkAdapterModule {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Injectable } from '@angular/core';

import { NbOverlayContainer } from '../overlay/mapping';


@Injectable()
export class NbOverlayContainerAdapter extends NbOverlayContainer {
protected _createContainer(): void {
const container = this._document.createElement('div');

container.classList.add('cdk-overlay-container');
this._document.querySelector('nb-layout').appendChild(container);
this._containerElement = container;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Injectable, NgZone } from '@angular/core';
import { CdkScrollable, ScrollDispatcher } from '@angular/cdk/overlay';
import { Observable } from 'rxjs';

import { NbPlatform } from '../overlay/mapping';
import { NbLayoutScrollService } from '../../../services/scroll.service';

@Injectable()
export class NbScrollDispatcherAdapter extends ScrollDispatcher {
constructor(ngZone: NgZone, platform: NbPlatform, protected scrollService: NbLayoutScrollService) {
super(ngZone, platform);
}

scrolled(auditTimeInMs?: number): Observable<CdkScrollable | void> {
return this.scrollService.onScroll();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Injectable, NgZone } from '@angular/core';
import { ViewportRuler } from '@angular/cdk/overlay';
import { map } from 'rxjs/operators';

import { NbPlatform } from '../overlay/mapping';
import { NbLayoutRulerService } from '../../../services/ruler.service';
import { NbLayoutScrollService, NbScrollPosition } from '../../../services/scroll.service';


@Injectable()
export class NbViewportRulerAdapter extends ViewportRuler {
constructor(platform: NbPlatform, ngZone: NgZone,
protected ruler: NbLayoutRulerService,
protected scroll: NbLayoutScrollService) {
super(platform, ngZone);
}

getViewportSize(): Readonly<{ width: number; height: number; }> {
let res;
/*
* getDimensions call is really synchronous operation.
* And we have to conform with the interface of the original service.
* */
this.ruler.getDimensions()
.pipe(map(dimensions => ({ width: dimensions.clientWidth, height: dimensions.clientHeight })))
.subscribe(rect => res = rect);
return res;
}

getViewportScrollPosition(): { left: number; top: number } {
let res;
/*
* getPosition call is really synchronous operation.
* And we have to conform with the interface of the original service.
* */
this.scroll.getPosition()
.pipe(map((position: NbScrollPosition) => ({ top: position.y, left: position.x })))
.subscribe(position => res = position);
return res;
}
}
2 changes: 2 additions & 0 deletions src/framework/theme/components/cdk/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './overlay/mapping';
export * from './overlay';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import '~@angular/cdk/overlay-prebuilt.css';

@mixin nb-overlay-theme {
.overlay-backdrop {
background: nb-theme(overlay-backdrop-bg);
}
}
6 changes: 6 additions & 0 deletions src/framework/theme/components/cdk/overlay/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './overlay.module';
export * from './overlay';
export * from './overlay-position';
export * from './overlay-container';
export * from './overlay-trigger';
export * from './mapping';
82 changes: 82 additions & 0 deletions src/framework/theme/components/cdk/overlay/mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Directive, Injectable, NgModule, TemplateRef, ViewContainerRef } from '@angular/core';
import { CdkPortal, ComponentPortal, Portal, PortalModule, TemplatePortal } from '@angular/cdk/portal';
import {
ComponentType,
ConnectedOverlayPositionChange,
ConnectedPosition,
ConnectionPositionPair,
FlexibleConnectedPositionStrategy,
GlobalPositionStrategy,
Overlay,
OverlayContainer,
OverlayModule,
OverlayPositionBuilder,
OverlayRef,
PositionStrategy,
} from '@angular/cdk/overlay';
import { Platform } from '@angular/cdk/platform';


@Directive({ selector: '[nbPortal]' })
export class NbPortalDirective extends CdkPortal {
}

@Injectable()
export class NbOverlayService extends Overlay {
}

@Injectable()
export class NbPlatform extends Platform {
}

@Injectable()
export class NbOverlayPositionBuilder extends OverlayPositionBuilder {
}

export class NbComponentPortal<T = any> extends ComponentPortal<T> {
}

export class NbTemplatePortal<T = any> extends TemplatePortal<T> {
constructor(template: TemplateRef<T>, viewContainerRef?: ViewContainerRef, context?: T) {
super(template, viewContainerRef, context);
}
}

export class NbOverlayContainer extends OverlayContainer {
}

export class NbFlexibleConnectedPositionStrategy extends FlexibleConnectedPositionStrategy {
}

export type NbPortal<T = any> = Portal<T>;
export type NbOverlayRef = OverlayRef;
export type NbComponentType<T = any> = ComponentType<T>;
export type NbGlobalPositionStrategy = GlobalPositionStrategy;
export type NbPositionStrategy = PositionStrategy;
export type NbConnectedPosition = ConnectedPosition;
export type NbConnectedOverlayPositionChange = ConnectedOverlayPositionChange;
export type NbConnectionPositionPair = ConnectionPositionPair;

const CDK_MODULES = [OverlayModule, PortalModule];

const CDK_PROVIDERS = [
NbOverlayService,
NbPlatform,
NbOverlayPositionBuilder,
];

/**
* This module helps us to keep all angular/cdk deps inside our cdk module via providing aliases.
* Approach will help us move cdk in separate npm package and refactor nebular/theme code.
* */
@NgModule({
imports: [...CDK_MODULES],
exports: [
...CDK_MODULES,
NbPortalDirective,
],
declarations: [NbPortalDirective],
providers: [...CDK_PROVIDERS],
})
export class NbCdkMappingModule {
}
90 changes: 90 additions & 0 deletions src/framework/theme/components/cdk/overlay/overlay-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { ChangeDetectorRef, Component, HostBinding, Input, TemplateRef, Type, ViewChild } from '@angular/core';
import { NgComponentOutlet } from '@angular/common';

import { NbPosition } from './overlay-position';

export abstract class NbPositionedContainer {
@Input() position: NbPosition;

@HostBinding('class.nb-overlay-top')
get top(): boolean {
return this.position === NbPosition.TOP
}

@HostBinding('class.nb-overlay-right')
get right(): boolean {
return this.position === NbPosition.RIGHT
}

@HostBinding('class.nb-overlay-bottom')
get bottom(): boolean {
return this.position === NbPosition.BOTTOM
}

@HostBinding('class.nb-overlay-left')
get left(): boolean {
return this.position === NbPosition.LEFT
}
}

@Component({
selector: 'nb-overlay-container',
template: `
<ng-container *ngIf="isTemplate">
<ng-container *ngTemplateOutlet="content; context: context"></ng-container>
</ng-container>
<ng-container *ngIf="isComponent" [ngComponentOutlet]="content"></ng-container>
<ng-container *ngIf="isPrimitive">
<div class="primitive-overlay">{{content}}</div>
</ng-container>
`,
})
export class NbOverlayContainerComponent {
@Input()
content: any;

@Input()
context: Object;

constructor(private cd: ChangeDetectorRef) {
}

@ViewChild(NgComponentOutlet)
set componentOutlet(el) {
if (this.isComponent) {
Object.assign(el._componentRef.instance, this.context);
/**
* Change detection has to be performed here, because another way applied context
* will be rendered on the next change detection loop and
* we'll have incorrect positioning. Because rendered component may change its size
* based on the context.
* */
this.cd.detectChanges();
}
}

/**
* Check that content is a TemplateRef.
*
* @return boolean
* */
get isTemplate(): boolean {
return this.content instanceof TemplateRef;
}

/**
* Check that content is an angular component.
*
* @return boolean
* */
get isComponent(): boolean {
return this.content instanceof Type;
}

/**
* Check that if content is not a TemplateRef or an angular component it means a primitive.
* */
get isPrimitive(): boolean {
return !this.isTemplate && !this.isComponent;
}
}
Loading

0 comments on commit b9283d3

Please sign in to comment.