Skip to content

Commit

Permalink
feat(modals): replaced component helper usage with component loader
Browse files Browse the repository at this point in the history
  • Loading branch information
valorkin committed Dec 12, 2016
1 parent 3a0df4c commit 1447fd3
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 106 deletions.
19 changes: 0 additions & 19 deletions demo/src/app/components/modal/docs/title.md
Original file line number Diff line number Diff line change
@@ -1,20 +1 @@
Modals are streamlined, but flexible, dialog prompts with the minimum required functionality and smart defaults.

Base specifications: [bootstrap 3](http://getbootstrap.com/javascript/#modals) or [bootstrap 4](http://v4-alpha.getbootstrap.com/components/modal/)

### **Important notes**:
- Don't forget to add `hack` to your application root component ([why?](https://github.com/angular/angular/issues/6446#issuecomment-173459525))

```typescript
import { Component, ViewContainerRef } from '@angular/core';

@Component({selector:'app-root'})
class AppRoot {
protected viewContainerRef: ViewContainerRef;

public constructor(viewContainerRef:ViewContainerRef) {
// You need this small hack in order to catch application root view container ref
this.viewContainerRef = viewContainerRef;
}
}
```
2 changes: 1 addition & 1 deletion src/component-loader/component-loader.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export class ComponentLoader<T> {
public onHidden: EventEmitter<any> = new EventEmitter();

public instance: T;
public _componentRef: ComponentRef<T>;

private _componentFactory: ComponentFactory<T>;
private _elementRef: ElementRef;
private _componentRef: ComponentRef<T>;
private _zoneSubscription: any;
private _contentRef: ContentRef;
private _viewContainerRef: ViewContainerRef;
Expand Down
2 changes: 1 addition & 1 deletion src/component-loader/component-loader.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class ComponentLoaderFactory {
* @param _renderer
* @returns {ComponentLoader}
*/
public createLoader<T>(_elementRef: ElementRef, _viewContainerRef: ViewContainerRef, _renderer: Renderer) {
public createLoader<T>(_elementRef: ElementRef, _viewContainerRef: ViewContainerRef, _renderer: Renderer):ComponentLoader<T> {
return new ComponentLoader<T>(_viewContainerRef, _renderer, _elementRef,
this._injector, this._componentFactoryResolver, this._ngZone, this._posService);
}
Expand Down
4 changes: 0 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,12 @@ import { TabsModule } from './tabs/tabs.module';
import { TimepickerModule } from './timepicker/timepicker.module';
import { TooltipModule } from './tooltip/tooltip.module';
import { TypeaheadModule } from './typeahead/typeahead.module';
import { ComponentsHelper } from './utils/components-helper.service';

@NgModule({
exports: [
AccordionModule, AlertModule, ButtonsModule, CarouselModule, CollapseModule, DatepickerModule, DropdownModule,
ModalModule, PaginationModule, ProgressbarModule, RatingModule, TabsModule, TimepickerModule, TooltipModule,
TypeaheadModule
],
providers: [
{provide: ComponentsHelper, useClass: ComponentsHelper}
]
})
export class Ng2BootstrapModule {
Expand Down
3 changes: 1 addition & 2 deletions src/modal/modal-backdrop.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ export class ModalBackdropComponent {
protected _isAnimated:boolean;
protected _isShown:boolean = false;

public constructor(options:ModalBackdropOptions, element:ElementRef, renderer:Renderer) {
public constructor(element:ElementRef, renderer:Renderer) {
this.element = element;
this.renderer = renderer;
this.isAnimated = options.animate !== false;
}
}
137 changes: 60 additions & 77 deletions src/modal/modal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ import {
Input,
OnDestroy,
Output,
Renderer
Renderer, ViewContainerRef
} from '@angular/core';

import { ComponentsHelper } from '../utils/components-helper.service';
import { document } from '../utils/facade/browser';

import { Utils } from '../utils/utils.class';
import { ModalBackdropComponent, ModalBackdropOptions } from './modal-backdrop.component';
import { ModalBackdropComponent } from './modal-backdrop.component';
import { ClassName, modalConfigDefaults, ModalOptions, Selector } from './modal-options.class';

import { window } from '../utils/facade/browser';
import { ComponentLoader } from '../component-loader/component-loader.class';
import { ComponentLoaderFactory } from '../component-loader/component-loader.factory';

const TRANSITION_DURATION = 300;
const BACKDROP_TRANSITION_DURATION = 150;
Expand Down Expand Up @@ -52,37 +55,29 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
return this._isShown;
}

// todo: implement _dialog
protected _dialog: any;

protected _config: ModalOptions;
protected _isShown: boolean = false;

protected isBodyOverflowing: boolean = false;
protected originalBodyPadding: number = 0;
protected scrollbarWidth: number = 0;

// reference to backdrop component
protected backdrop: ComponentRef<ModalBackdropComponent>;

protected timerHideModal: number = 0;
protected timerRmBackDrop: number = 0;

// constructor props
protected element: ElementRef;
protected renderer: Renderer;
protected componentsHelper: ComponentsHelper;
protected _element: ElementRef;
protected _renderer: Renderer;

protected get document(): any {
return this.componentsHelper.getDocument();
}

/** Host element manipulations */
// @HostBinding(`class.${ClassName.IN}`) protected _addClassIn:boolean;
// reference to backdrop component
protected backdrop: ComponentRef<ModalBackdropComponent>;
private _backdrop: ComponentLoader<ModalBackdropComponent>;
// todo: implement _dialog
private _dialog: any;

@HostListener('click', ['$event'])
public onClick(event: any): void {
if (this.config.ignoreBackdropClick || this.config.backdrop === 'static' || event.target !== this.element.nativeElement) {
if (this.config.ignoreBackdropClick || this.config.backdrop === 'static' || event.target !== this._element.nativeElement) {
return;
}

Expand All @@ -97,40 +92,31 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
}
}

public constructor(element: ElementRef, renderer: Renderer, componentsHelper: ComponentsHelper) {
this.element = element;
this.renderer = renderer;
this.componentsHelper = componentsHelper;
public constructor(_element: ElementRef, _viewContainerRef: ViewContainerRef, _renderer: Renderer, clf: ComponentLoaderFactory) {
this._element = _element;
this._renderer = _renderer;
this._backdrop = clf.createLoader<ModalBackdropComponent>(_element, _viewContainerRef, _renderer);
}

public ngOnDestroy(): any {
this.config = void 0;
// this._element = null
// this._dialog = null
// this._backdrop = null
if (this._isShown) {
this._isShown = false;
this.hideModal();
this._backdrop.dispose();
}
this._isShown = void 0;
this.isBodyOverflowing = void 0;
this.originalBodyPadding = void 0;
this.scrollbarWidth = void 0;
this.timerHideModal = void 0;
this.timerRmBackDrop = void 0;
}

public ngAfterViewInit(): any {
this._config = this._config || this.getConfig();
}

/** Public methods */

public toggle(/*relatedTarget?:ViewContainerRef*/): void {
return this._isShown ? this.hide() : this.show(/*relatedTarget*/);
public toggle(): void {
return this._isShown ? this.hide() : this.show();
}

public show(/*relatedTarget?:ViewContainerRef*/): void {
public show(): void {
this.onShow.emit(this);
if (this._isShown) {
return;
Expand All @@ -143,12 +129,12 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
this.checkScrollbar();
this.setScrollbar();

if (this.document && this.document.body) {
this.renderer.setElementClass(this.document.body, ClassName.OPEN, true);
if (document && document.body) {
this._renderer.setElementClass(document.body, ClassName.OPEN, true);
}

this.showBackdrop(() => {
this.showElement(/*relatedTarget*/);
this.showElement();
});
}

Expand All @@ -168,8 +154,8 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
clearTimeout(this.timerRmBackDrop);

this._isShown = false;
this.renderer.setElementClass(this.element.nativeElement, ClassName.IN, false);
this.renderer.setElementClass(this.element.nativeElement, ClassName.ACTIVE, false);
this._renderer.setElementClass(this._element.nativeElement, ClassName.IN, false);
this._renderer.setElementClass(this._element.nativeElement, ClassName.ACTIVE, false);
// this._addClassIn = false;

if (this.isAnimated) {
Expand All @@ -187,31 +173,31 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
/**
* Show dialog
*/
protected showElement(/*relatedTarget?:ViewContainerRef*/): void {
// todo: replace this with component helper usage `add to root`
if (!this.element.nativeElement.parentNode ||
(this.element.nativeElement.parentNode.nodeType !== Node.ELEMENT_NODE)) {
protected showElement(): void {
// todo: replace this with component loader usage
if (!this._element.nativeElement.parentNode ||
(this._element.nativeElement.parentNode.nodeType !== Node.ELEMENT_NODE)) {
// don't move modals dom position
if (this.document && this.document.body) {
this.document.body.appendChild(this.element.nativeElement);
if (document && document.body) {
document.body.appendChild(this._element.nativeElement);
}
}

this.renderer.setElementAttribute(this.element.nativeElement, 'aria-hidden', 'false');
this.renderer.setElementStyle(this.element.nativeElement, 'display', 'block');
this.renderer.setElementProperty(this.element.nativeElement, 'scrollTop', 0);
this._renderer.setElementAttribute(this._element.nativeElement, 'aria-hidden', 'false');
this._renderer.setElementStyle(this._element.nativeElement, 'display', 'block');
this._renderer.setElementProperty(this._element.nativeElement, 'scrollTop', 0);

if (this.isAnimated) {
Utils.reflow(this.element.nativeElement);
Utils.reflow(this._element.nativeElement);
}

// this._addClassIn = true;
this.renderer.setElementClass(this.element.nativeElement, ClassName.IN, true);
this.renderer.setElementClass(this.element.nativeElement, ClassName.ACTIVE, true);
this._renderer.setElementClass(this._element.nativeElement, ClassName.IN, true);
this._renderer.setElementClass(this._element.nativeElement, ClassName.ACTIVE, true);

const transitionComplete = () => {
if (this._config.focus) {
this.element.nativeElement.focus();
this._element.nativeElement.focus();
}
this.onShown.emit(this);
};
Expand All @@ -224,11 +210,11 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
}

protected hideModal(): void {
this.renderer.setElementAttribute(this.element.nativeElement, 'aria-hidden', 'true');
this.renderer.setElementStyle(this.element.nativeElement, 'display', 'none');
this._renderer.setElementAttribute(this._element.nativeElement, 'aria-hidden', 'true');
this._renderer.setElementStyle(this._element.nativeElement, 'display', 'none');
this.showBackdrop(() => {
if (this.document && this.document.body) {
this.renderer.setElementClass(this.document.body, ClassName.OPEN, false);
if (document && document.body) {
this._renderer.setElementClass(document.body, ClassName.OPEN, false);
}
this.resetAdjustments();
this.resetScrollbar();
Expand All @@ -240,11 +226,11 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
protected showBackdrop(callback?: Function): void {
if (this._isShown && this.config.backdrop && (!this.backdrop || !this.backdrop.instance.isShown)) {
this.removeBackdrop();
this.backdrop = this.componentsHelper
.appendNextToRoot(
ModalBackdropComponent,
ModalBackdropOptions,
new ModalBackdropOptions({animate: false}));
this._backdrop
.attach(ModalBackdropComponent)
.to('body')
.show(null, {isAnimated: false});
this.backdrop = this._backdrop._componentRef;

if (this.isAnimated) {
this.backdrop.instance.isAnimated = this.isAnimated;
Expand Down Expand Up @@ -283,10 +269,7 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
}

protected removeBackdrop(): void {
if (this.backdrop) {
this.backdrop.destroy();
this.backdrop = void 0;
}
this._backdrop.hide();
}

/** Events tricks */
Expand Down Expand Up @@ -315,46 +298,46 @@ export class ModalDirective implements AfterViewInit, OnDestroy {
// }

protected resetAdjustments(): void {
this.renderer.setElementStyle(this.element.nativeElement, 'paddingLeft', '');
this.renderer.setElementStyle(this.element.nativeElement, 'paddingRight', '');
this._renderer.setElementStyle(this._element.nativeElement, 'paddingLeft', '');
this._renderer.setElementStyle(this._element.nativeElement, 'paddingRight', '');
}

/** Scroll bar tricks */

protected checkScrollbar(): void {
this.isBodyOverflowing = this.document.body.clientWidth < window.innerWidth;
this.isBodyOverflowing = document.body.clientWidth < window.innerWidth;
this.scrollbarWidth = this.getScrollbarWidth();
}

protected setScrollbar(): void {
if (!this.document) {
if (!document) {
return;
}

const fixedEl = this.document.querySelector(Selector.FIXED_CONTENT);
const fixedEl = document.querySelector(Selector.FIXED_CONTENT);

if (!fixedEl) {
return;
}

const bodyPadding = parseInt(Utils.getStyles(fixedEl).paddingRight || 0, 10);
this.originalBodyPadding = parseInt(this.document.body.style.paddingRight || 0, 10);
this.originalBodyPadding = parseInt(document.body.style.paddingRight || 0, 10);

if (this.isBodyOverflowing) {
this.document.body.style.paddingRight = `${bodyPadding + this.scrollbarWidth}px`;
document.body.style.paddingRight = `${bodyPadding + this.scrollbarWidth}px`;
}
}

protected resetScrollbar(): void {
this.document.body.style.paddingRight = this.originalBodyPadding;
document.body.style.paddingRight = this.originalBodyPadding;
}

// thx d.walsh
protected getScrollbarWidth(): number {
const scrollDiv = this.renderer.createElement(this.document.body, 'div', void 0);
const scrollDiv = this._renderer.createElement(document.body, 'div', void 0);
scrollDiv.className = ClassName.SCROLLBAR_MEASURER;
const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
this.document.body.removeChild(scrollDiv);
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
}
5 changes: 3 additions & 2 deletions src/modal/modal.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { NgModule } from '@angular/core';

import { ModalBackdropComponent } from './modal-backdrop.component';
import { ModalDirective } from './modal.component';
import { ComponentsHelper } from '../utils/components-helper.service';
import { PositioningService } from '../positioning';
import { ComponentLoaderFactory } from '../component-loader';

@NgModule({
declarations: [ModalBackdropComponent, ModalDirective],
exports: [ModalBackdropComponent, ModalDirective],
entryComponents: [ModalBackdropComponent],
providers: [ComponentsHelper]
providers: [ComponentLoaderFactory, PositioningService]
})
export class ModalModule {
}

0 comments on commit 1447fd3

Please sign in to comment.