ngx-bootstrap
Version:
Angular Bootstrap
350 lines (343 loc) • 14.4 kB
JavaScript
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