UNPKG

ngx-bootstrap

Version:
350 lines (343 loc) 14.4 kB
import * as i0 from '@angular/core'; import { EventEmitter, Injector, ElementRef, TemplateRef, Injectable, Inject } from '@angular/core'; import { listenToTriggersV2, registerOutsideClick, registerEscClick } from 'ngx-bootstrap/utils'; import * as i1 from 'ngx-bootstrap/positioning'; import { DOCUMENT } from '@angular/common'; class BsComponentRef { } /** * @copyright Valor Software * @copyright Angular ng-bootstrap team */ class ContentRef { constructor( // eslint-disable-next-line @typescript-eslint/no-explicit-any nodes, viewRef, // eslint-disable-next-line @typescript-eslint/no-explicit-any componentRef) { this.nodes = nodes; this.viewRef = viewRef; this.componentRef = componentRef; } } // todo: add delay support // todo: merge events onShow, onShown, etc... // todo: add global positioning configuration? class ComponentLoader { /** * Do not use this directly, it should be instanced via * `ComponentLoadFactory.attach` * @internal */ constructor(_viewContainerRef, _renderer, _elementRef, _injector, _componentFactoryResolver, _ngZone, _applicationRef, _posService, _document) { this._viewContainerRef = _viewContainerRef; this._renderer = _renderer; this._elementRef = _elementRef; this._injector = _injector; this._componentFactoryResolver = _componentFactoryResolver; this._ngZone = _ngZone; this._applicationRef = _applicationRef; this._posService = _posService; this._document = _document; this.onBeforeShow = new EventEmitter(); this.onShown = new EventEmitter(); this.onBeforeHide = new EventEmitter(); this.onHidden = new EventEmitter(); this._providers = []; this._isHiding = false; /** * A selector used if container element was not found */ this.containerDefaultSelector = 'body'; this._listenOpts = {}; this._globalListener = Function.prototype; } get isShown() { if (this._isHiding) { return false; } return !!this._componentRef; } attach(compType) { this._componentFactory = this._componentFactoryResolver .resolveComponentFactory(compType); return this; } // todo: add behaviour: to target element, `body`, custom element to(container) { this.container = container || this.container; return this; } position(opts) { if (!opts) { return this; } this.attachment = opts.attachment || this.attachment; this._elementRef = opts.target || this._elementRef; return this; } provide(provider) { this._providers.push(provider); return this; } // todo: appendChild to element or document.querySelector(this.container) show(opts = {}) { this._subscribePositioning(); this._innerComponent = void 0; if (!this._componentRef) { this.onBeforeShow.emit(); this._contentRef = this._getContentRef(opts.content, opts.context, opts.initialState); const injector = Injector.create({ providers: this._providers, parent: this._injector }); if (!this._componentFactory) { return; } this._componentRef = this._componentFactory.create(injector, this._contentRef.nodes); this._applicationRef.attachView(this._componentRef.hostView); // this._componentRef = this._viewContainerRef // .createComponent(this._componentFactory, 0, injector, this._contentRef.nodes); this.instance = this._componentRef.instance; Object.assign(this._componentRef.instance, opts); if (this.container instanceof ElementRef) { this.container.nativeElement.appendChild(this._componentRef.location.nativeElement); } if (typeof this.container === 'string' && typeof this._document !== 'undefined') { const selectedElement = this._document.querySelector(this.container) || this._document.querySelector(this.containerDefaultSelector); if (!selectedElement) { return; } selectedElement.appendChild(this._componentRef.location.nativeElement); } if (!this.container && this._elementRef && this._elementRef.nativeElement.parentElement) { this._elementRef.nativeElement.parentElement.appendChild(this._componentRef.location.nativeElement); } // we need to manually invoke change detection since events registered // via // Renderer::listen() are not picked up by change detection with the // OnPush strategy if (this._contentRef.componentRef) { this._innerComponent = this._contentRef.componentRef.instance; this._contentRef.componentRef.changeDetectorRef.markForCheck(); this._contentRef.componentRef.changeDetectorRef.detectChanges(); } this._componentRef.changeDetectorRef.markForCheck(); this._componentRef.changeDetectorRef.detectChanges(); this.onShown.emit(opts.id ? { id: opts.id } : this._componentRef.instance); } this._registerOutsideClick(); return this._componentRef; } hide(id) { if (!this._componentRef) { return this; } this._posService.deletePositionElement(this._componentRef.location); this.onBeforeHide.emit(this._componentRef.instance); const componentEl = this._componentRef.location.nativeElement; componentEl.parentNode?.removeChild(componentEl); this._contentRef?.componentRef?.destroy(); if (this._viewContainerRef && this._contentRef?.viewRef) { this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._contentRef.viewRef)); } this._contentRef?.viewRef?.destroy(); this._componentRef?.destroy(); this._contentRef = void 0; this._componentRef = void 0; this._removeGlobalListener(); this.onHidden.emit(id ? { id } : null); return this; } toggle() { if (this.isShown) { this.hide(); return; } this.show(); } dispose() { if (this.isShown) { this.hide(); } this._unsubscribePositioning(); if (this._unregisterListenersFn) { this._unregisterListenersFn(); } } listen(listenOpts) { this.triggers = listenOpts.triggers || this.triggers; this._listenOpts.outsideClick = listenOpts.outsideClick; this._listenOpts.outsideEsc = listenOpts.outsideEsc; listenOpts.target = listenOpts.target || this._elementRef?.nativeElement; const hide = (this._listenOpts.hide = () => listenOpts.hide ? listenOpts.hide() : void this.hide()); const show = (this._listenOpts.show = (registerHide) => { listenOpts.show ? listenOpts.show(registerHide) : this.show(registerHide); registerHide(); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const toggle = (registerHide) => { this.isShown ? hide() : show(registerHide); }; if (this._renderer) { this._unregisterListenersFn = listenToTriggersV2(this._renderer, { target: listenOpts.target, triggers: listenOpts.triggers, show, hide, toggle }); } return this; } _removeGlobalListener() { if (this._globalListener) { this._globalListener(); this._globalListener = Function.prototype; } } attachInline(vRef, // eslint-disable-next-line @typescript-eslint/no-explicit-any template) { if (vRef && template) { this._inlineViewRef = vRef.createEmbeddedView(template); } return this; } _registerOutsideClick() { if (!this._componentRef || !this._componentRef.location) { return; } let unsubscribeOutsideClick = Function.prototype; let unsubscribeEscClick = Function.prototype; // why: should run after first event bubble if (this._listenOpts.outsideClick) { const target = this._componentRef.location.nativeElement; setTimeout(() => { if (this._renderer && this._elementRef) { unsubscribeOutsideClick = registerOutsideClick(this._renderer, { targets: [target, this._elementRef.nativeElement], outsideClick: this._listenOpts.outsideClick, hide: () => this._listenOpts.hide && this._listenOpts.hide() }); } }); } if (this._listenOpts.outsideEsc && this._renderer && this._elementRef) { const target = this._componentRef.location.nativeElement; unsubscribeEscClick = registerEscClick(this._renderer, { targets: [target, this._elementRef.nativeElement], outsideEsc: this._listenOpts.outsideEsc, hide: () => this._listenOpts.hide && this._listenOpts.hide() }); } this._globalListener = () => { unsubscribeOutsideClick(); unsubscribeEscClick(); }; } getInnerComponent() { return this._innerComponent; } _subscribePositioning() { if (this._zoneSubscription || !this.attachment) { return; } this.onShown.subscribe(() => { this._posService.position({ element: this._componentRef?.location, target: this._elementRef, attachment: this.attachment, appendToBody: this.container === 'body' }); }); this._zoneSubscription = this._ngZone.onStable.subscribe(() => { if (!this._componentRef) { return; } this._posService.calcPosition(); }); } _unsubscribePositioning() { if (!this._zoneSubscription) { return; } this._zoneSubscription.unsubscribe(); this._zoneSubscription = void 0; } _getContentRef( // eslint-disable-next-line @typescript-eslint/no-explicit-any content, // eslint-disable-next-line @typescript-eslint/no-explicit-any context, // eslint-disable-next-line @typescript-eslint/no-explicit-any initialState) { if (!content) { return new ContentRef([]); } if (content instanceof TemplateRef) { if (this._viewContainerRef) { const _viewRef = this._viewContainerRef .createEmbeddedView(content, context); _viewRef.markForCheck(); return new ContentRef([_viewRef.rootNodes], _viewRef); } const viewRef = content.createEmbeddedView({}); this._applicationRef.attachView(viewRef); return new ContentRef([viewRef.rootNodes], viewRef); } if (typeof content === 'function') { const contentCmptFactory = this._componentFactoryResolver.resolveComponentFactory(content); const modalContentInjector = Injector.create({ providers: this._providers, parent: this._injector }); const componentRef = contentCmptFactory.create(modalContentInjector); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Object.assign(componentRef.instance, initialState); this._applicationRef.attachView(componentRef.hostView); return new ContentRef([[componentRef.location.nativeElement]], componentRef.hostView, componentRef); } const nodes = this._renderer ? [this._renderer.createText(`${content}`)] : []; return new ContentRef([nodes]); } } class ComponentLoaderFactory { constructor(_componentFactoryResolver, _ngZone, _injector, _posService, _applicationRef, _document) { this._componentFactoryResolver = _componentFactoryResolver; this._ngZone = _ngZone; this._injector = _injector; this._posService = _posService; this._applicationRef = _applicationRef; this._document = _document; } /** * * @param _elementRef * @param _viewContainerRef * @param _renderer */ createLoader(_elementRef, _viewContainerRef, _renderer) { return new ComponentLoader(_viewContainerRef, _renderer, _elementRef, this._injector, this._componentFactoryResolver, this._ngZone, this._applicationRef, this._posService, this._document); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: ComponentLoaderFactory, deps: [{ token: i0.ComponentFactoryResolver }, { token: i0.NgZone }, { token: i0.Injector }, { token: i1.PositioningService }, { token: i0.ApplicationRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: ComponentLoaderFactory, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: ComponentLoaderFactory, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i0.ComponentFactoryResolver }, { type: i0.NgZone }, { type: i0.Injector }, { type: i1.PositioningService }, { type: i0.ApplicationRef }, { type: Document, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); /** * Generated bundle index. Do not edit. */ export { BsComponentRef, ComponentLoader, ComponentLoaderFactory, ContentRef }; //# sourceMappingURL=ngx-bootstrap-component-loader.mjs.map