UNPKG

ap-popup

Version:

Flexible Angular popup/notification library

268 lines (263 loc) 17.2 kB
import * as i0 from '@angular/core'; import { EventEmitter, HostBinding, Output, Input, Component, Inject, Injectable } from '@angular/core'; import { Subject, timer, takeUntil, take } from 'rxjs'; import { NgIf, NgClass, NgTemplateOutlet, DOCUMENT } from '@angular/common'; class ApPopupComponent { elementRef; text; template; templateContext = {}; theme = 'default'; animation = 'cubic'; animationDuration = 900; animationHideDuration = 900; timeout = 3000; parentSelector = null; closeByMouseUp = true; customClass; customWrapperClass; zIndex; remove = new EventEmitter(); hideAnimation; distanceToPageTop = 0; id; cubicBezierFunctionIn = 'cubic-bezier(0, 1.82, 0.46, 0.84)'; cubicBezierFunctionOut = 'cubic-bezier(0.42, -0.65, 0.27, 0.93)'; _close$ = new Subject(); get getHostTransition() { let timingFunction = 'linear'; if (this.animation === 'cubic') { timingFunction = this.cubicBezierFunctionIn; } return `all ${timingFunction} ${this.animationDuration}ms`; } get getZIndex() { return this.zIndex?.toString() || '1'; } get getCursor() { return this.closeByMouseUp ? 'pointer' : 'default'; } constructor(elementRef) { this.elementRef = elementRef; } ngOnInit() { if (!this.timeout) return; timer(this.timeout) .pipe(takeUntil(this._close$)) .subscribe(() => { this._startHideAnimation(); }); } ngOnDestroy() { this._close$.complete(); } onMouseUp() { if (!this.closeByMouseUp) return; this.close(); } close() { this.zIndex = 999; this._close$.next(); this._startHideAnimation(); } updateDistanceTop() { this.distanceToPageTop = this._getDistanceFromBottomToPageTop(this.elementRef.nativeElement); } _startHideAnimation() { this.updateDistanceTop(); this.hideAnimation = `${this.animation}-out`; this.remove.emit(); } _getDistanceFromBottomToPageTop(element) { return element.getBoundingClientRect().bottom; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ApPopupComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ApPopupComponent, isStandalone: true, selector: "lib-ap-popup", inputs: { text: "text", template: "template", templateContext: "templateContext", theme: "theme", animation: "animation", animationDuration: "animationDuration", animationHideDuration: "animationHideDuration", timeout: "timeout", parentSelector: "parentSelector", closeByMouseUp: "closeByMouseUp", customClass: "customClass", customWrapperClass: "customWrapperClass", zIndex: "zIndex" }, outputs: { remove: "remove" }, host: { properties: { "style.transition": "this.getHostTransition", "style.zIndex": "this.getZIndex", "style.cursor": "this.getCursor" } }, ngImport: i0, template: "<div\r\n class=\"ap-popup\"\r\n [ngClass]=\"[animation, hideAnimation ? hideAnimation : '', customClass ? customClass : '']\"\r\n [style]=\"\r\n '--animationDuration:' +\r\n animationDuration +\r\n 'ms;' +\r\n '--cubicFunctionIn:' +\r\n cubicBezierFunctionIn +\r\n ';' +\r\n '--cubicFunctionOut:' +\r\n cubicBezierFunctionOut +\r\n ';' +\r\n '--distanceToPageTop:' +\r\n distanceToPageTop +\r\n 'px;'\r\n \"\r\n (mouseup)=\"onMouseUp()\"\r\n>\r\n <div *ngIf=\"text || template\" class=\"ap-popup__content\" [ngClass]=\"'ap-popup__content-' + theme\">\r\n <ng-container\r\n *ngIf=\"template\"\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"templateContext\"\r\n ></ng-container>\r\n\r\n <span *ngIf=\"text && !template\" [innerHtml]=\"text\"></span>\r\n </div>\r\n</div>\r\n", styles: [":host{--ap-popup-max-width: 540px;max-width:var(--ap-popup-max-width);display:flex;width:fit-content;position:absolute;top:0;left:0;right:0;margin-inline:auto;justify-content:center;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}.ap-popup{width:auto;max-width:fit-content}.ap-popup__content{display:flex;align-items:center;column-gap:8px;padding:12px 16px;border-radius:16px;background:#fff;box-shadow:0 4px 12px #04040414;width:fit-content;font-size:16px;font-weight:400;line-height:24px}.ap-popup__content-warning,.ap-popup__content-error{background:#fff3d1;box-shadow:0 4px 12px #8f701614}.ap-popup__content-success{background:#e9f1df;box-shadow:0 4px 12px #4a862e14}.ap-popup__content-light{background:#000;box-shadow:0 8px 28px #00000014}.slide{animation:slideInFromRight var(--animationDuration) forwards ease-in}.slide-out{animation:slideOutToRight var(--animationDuration) forwards ease-out}.cubic{animation:slideInFromTop var(--animationDuration) forwards var(--cubicFunctionIn)}.cubic-out{animation:slideOutToTop var(--animationDuration) forwards var(--cubicFunctionOut)}.fade{animation:fadeIn var(--animationDuration) forwards linear}.fade-out{animation:fadeOut var(--animationDuration) forwards linear}@keyframes slideInFromTop{0%{transform:translateY(calc(-200% - var(--distanceToPageTop)))}to{transform:translateY(0)}}@keyframes slideOutToTop{0%{transform:translateY(0)}to{transform:translateY(calc(-200% - var(--distanceToPageTop)))}}@keyframes slideInFromRight{0%{transform:translate(110%)}to{transform:translate(0)}}@keyframes slideOutToRight{0%{transform:translate(0)}to{transform:translate(110%)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ApPopupComponent, decorators: [{ type: Component, args: [{ selector: 'lib-ap-popup', imports: [NgIf, NgClass, NgTemplateOutlet], template: "<div\r\n class=\"ap-popup\"\r\n [ngClass]=\"[animation, hideAnimation ? hideAnimation : '', customClass ? customClass : '']\"\r\n [style]=\"\r\n '--animationDuration:' +\r\n animationDuration +\r\n 'ms;' +\r\n '--cubicFunctionIn:' +\r\n cubicBezierFunctionIn +\r\n ';' +\r\n '--cubicFunctionOut:' +\r\n cubicBezierFunctionOut +\r\n ';' +\r\n '--distanceToPageTop:' +\r\n distanceToPageTop +\r\n 'px;'\r\n \"\r\n (mouseup)=\"onMouseUp()\"\r\n>\r\n <div *ngIf=\"text || template\" class=\"ap-popup__content\" [ngClass]=\"'ap-popup__content-' + theme\">\r\n <ng-container\r\n *ngIf=\"template\"\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"templateContext\"\r\n ></ng-container>\r\n\r\n <span *ngIf=\"text && !template\" [innerHtml]=\"text\"></span>\r\n </div>\r\n</div>\r\n", styles: [":host{--ap-popup-max-width: 540px;max-width:var(--ap-popup-max-width);display:flex;width:fit-content;position:absolute;top:0;left:0;right:0;margin-inline:auto;justify-content:center;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}.ap-popup{width:auto;max-width:fit-content}.ap-popup__content{display:flex;align-items:center;column-gap:8px;padding:12px 16px;border-radius:16px;background:#fff;box-shadow:0 4px 12px #04040414;width:fit-content;font-size:16px;font-weight:400;line-height:24px}.ap-popup__content-warning,.ap-popup__content-error{background:#fff3d1;box-shadow:0 4px 12px #8f701614}.ap-popup__content-success{background:#e9f1df;box-shadow:0 4px 12px #4a862e14}.ap-popup__content-light{background:#000;box-shadow:0 8px 28px #00000014}.slide{animation:slideInFromRight var(--animationDuration) forwards ease-in}.slide-out{animation:slideOutToRight var(--animationDuration) forwards ease-out}.cubic{animation:slideInFromTop var(--animationDuration) forwards var(--cubicFunctionIn)}.cubic-out{animation:slideOutToTop var(--animationDuration) forwards var(--cubicFunctionOut)}.fade{animation:fadeIn var(--animationDuration) forwards linear}.fade-out{animation:fadeOut var(--animationDuration) forwards linear}@keyframes slideInFromTop{0%{transform:translateY(calc(-200% - var(--distanceToPageTop)))}to{transform:translateY(0)}}@keyframes slideOutToTop{0%{transform:translateY(0)}to{transform:translateY(calc(-200% - var(--distanceToPageTop)))}}@keyframes slideInFromRight{0%{transform:translate(110%)}to{transform:translate(0)}}@keyframes slideOutToRight{0%{transform:translate(0)}to{transform:translate(110%)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { text: [{ type: Input }], template: [{ type: Input }], templateContext: [{ type: Input }], theme: [{ type: Input }], animation: [{ type: Input }], animationDuration: [{ type: Input }], animationHideDuration: [{ type: Input }], timeout: [{ type: Input }], parentSelector: [{ type: Input }], closeByMouseUp: [{ type: Input }], customClass: [{ type: Input }], customWrapperClass: [{ type: Input }], zIndex: [{ type: Input }], remove: [{ type: Output }], getHostTransition: [{ type: HostBinding, args: ['style.transition'] }], getZIndex: [{ type: HostBinding, args: ['style.zIndex'] }], getCursor: [{ type: HostBinding, args: ['style.cursor'] }] } }); class ApPopupService { _componentFactoryResolver; _appRef; _injector; _document; _zIndexCounter = 1000; _wrapper = null; _renderer; _popups = []; _removingPopupIds = new Set(); _customWrapperClass; constructor(rendererFactory, _componentFactoryResolver, _appRef, _injector, _document) { this._componentFactoryResolver = _componentFactoryResolver; this._appRef = _appRef; this._injector = _injector; this._document = _document; this._renderer = rendererFactory.createRenderer(null, null); } openPopup(data) { if (!this._popups.length) { if (this._wrapper) { this._renderer.removeChild(this._document.body, this._wrapper); } this._wrapper = null; } if (!this._wrapper) { this._wrapper = this._renderer.createElement('div'); this._renderer.addClass(this._wrapper, 'popups-wrapper'); this._customWrapperClass = data.customWrapperClass; if (this._customWrapperClass) { this._renderer.addClass(this._wrapper, this._customWrapperClass); } let parent = this._document.body; if (data.parentSelector) { const parentRef = this._document.querySelector(data.parentSelector); if (parentRef) { parent = parentRef; } } this._renderer.appendChild(parent, this._wrapper); } const componentFactory = this._componentFactoryResolver.resolveComponentFactory(ApPopupComponent); const componentRef = componentFactory.create(this._injector); componentRef.instance.text = data.text; componentRef.instance.template = data.template; componentRef.instance.customClass = data.customClass; componentRef.instance.customWrapperClass = data.customWrapperClass; componentRef.instance.zIndex = ++this._zIndexCounter; componentRef.instance.id = this._zIndexCounter; componentRef.instance.theme = data.theme || componentRef.instance.theme; componentRef.instance.templateContext = data.templateContext || componentRef.instance.templateContext; componentRef.instance.animation = data.animation || componentRef.instance.animation; componentRef.instance.timeout = data.timeout === null ? null : data.timeout || componentRef.instance.timeout; componentRef.instance.parentSelector = data.parentSelector || componentRef.instance.parentSelector; componentRef.instance.animationDuration = data.animationDuration || componentRef.instance.animationDuration; componentRef.instance.closeByMouseUp = data.closeByMouseUp === undefined ? componentRef.instance.closeByMouseUp : data.closeByMouseUp; componentRef.instance.remove.pipe(take(1)).subscribe(() => { this.removePopup(componentRef.instance); }); this._appRef.attachView(componentRef.hostView); const domElem = componentRef.hostView.rootNodes[0]; this._renderer.appendChild(this._wrapper, domElem); const animatedPopups = this._popups.filter((p) => !p.hideAnimation); animatedPopups.forEach((popup, index) => { const previousPopupsHeight = animatedPopups .filter((_, i) => i < index) .reduce((acc, p) => { return acc + p.elementRef.nativeElement.clientHeight; }, 0); setTimeout(() => { this._renderer.setStyle(popup.elementRef.nativeElement, 'transform', `translateY(calc(${previousPopupsHeight + componentRef.instance.elementRef.nativeElement.clientHeight}px + ${12 * (index + 1)}px))`); }, 0); popup.updateDistanceTop(); }); this._popups.unshift(componentRef.instance); return componentRef.instance; } removePopup(popup) { const index = this._popups.findIndex((p) => p.id === popup.id); if (index < 0) return; this._removingPopupIds.add(popup.id); timer(popup.animationDuration / 4) .pipe(take(1)) .subscribe(() => { const newList = this._popups.filter((p) => !this._removingPopupIds.has(p.id)); for (let i = index; i < newList.length; i++) { const currentPopup = newList[i]; const previousPopupsHeight = newList .filter((_, pi) => pi < i) .reduce((acc, p) => { return acc + p.elementRef.nativeElement.clientHeight; }, 0); this._renderer.setStyle(currentPopup.elementRef.nativeElement, 'transform', `translateY(calc(${previousPopupsHeight}px + ${12 * i}px))`); } this._popups = this._popups.filter((p) => p.id !== popup.id); }); timer(popup.animationDuration) .pipe(take(1)) .subscribe(() => { this._renderer.removeChild(parent, popup.elementRef.nativeElement); this._removingPopupIds.delete(popup.id); if (!this._popups.length && this._wrapper && !this._removingPopupIds.size) { this._renderer.removeChild(this._document.body, this._wrapper); this._wrapper = null; } }); } clearAll() { if (this._wrapper) { this._renderer.removeChild(this._document.body, this._wrapper); } this._popups.length = 0; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ApPopupService, deps: [{ token: i0.RendererFactory2 }, { token: i0.ComponentFactoryResolver }, { token: i0.ApplicationRef }, { token: i0.Injector }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ApPopupService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ApPopupService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: i0.ComponentFactoryResolver }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: Document, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); /** * Generated bundle index. Do not edit. */ export { ApPopupComponent, ApPopupService }; //# sourceMappingURL=ap-popup.mjs.map