UNPKG

ionic4-tooltips

Version:

Tooltips module for Ionic 2+ and Angular 8+ apps

406 lines (398 loc) 14.4 kB
import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy, ElementRef, Renderer2, HostBinding, Input, ɵɵdefineInjectable, Injectable, Directive, ApplicationRef, ComponentFactoryResolver, ViewContainerRef, HostListener, NgModule } from '@angular/core'; import { trigger, state, style, transition, animate } from '@angular/animations'; import { Platform } from '@ionic/angular'; class TooltipBoxComponent { constructor(elementRef, rnd) { this.elementRef = elementRef; this.rnd = rnd; this.fadeState = 'invisible'; this.role = 'status'; this.tooltipStyles = {}; this.init = new Promise((resolve) => { this.initResolve = resolve; }); } set arrow(side) { this.rnd.setAttribute(this.getNativeElement(), 'class', 'has-arrow ' + 'arrow-' + side); } set posTop(val) { this.rnd.setStyle(this.getNativeElement(), 'top', val + 'px'); } set posLeft(val) { this.rnd.setStyle(this.getNativeElement(), 'left', val + 'px'); } getNativeElement() { return this.elementRef.nativeElement; } ngAfterViewInit() { this.initResolve(); } } TooltipBoxComponent.decorators = [ { type: Component, args: [{ selector: 'tooltip-box', template: "<div class=\"tooltip-box\"\n [attr.aria-role]=\"role\"\n [ngStyle]=\"tooltipStyles\">\n <div *ngIf=\"tooltipHtml; else txt\" [innerHTML]=\"tooltipHtml\"></div>\n\n <ng-template #txt><div [innerHtml]=\"text\"></div></ng-template>\n</div>\n", animations: [ trigger('fade', [ state('visible', style({ opacity: 1 })), state('invisible', style({ opacity: 0 })), transition('visible <=> invisible', animate('300ms linear')) ]) ], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{background-color:rgba(0,0,0,.8);color:#fff;display:inline-block;font-size:15px;padding:15px 25px;position:fixed;z-index:3}:host.has-arrow:before{border:5px solid transparent;content:\"\";height:0;position:absolute;width:0}:host.has-arrow.arrow-top:before{border-bottom:5px solid rgba(0,0,0,.8);top:-10px}:host.has-arrow.arrow-left:before{border-right:5px solid rgba(0,0,0,.8);left:-10px}:host.has-arrow.arrow-right:before{border-left:5px solid rgba(0,0,0,.8);right:-10px}:host.has-arrow.arrow-bottom:before{border-top:5px solid rgba(0,0,0,.8);bottom:-10px}"] },] } ]; TooltipBoxComponent.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 } ]; TooltipBoxComponent.propDecorators = { fadeState: [{ type: HostBinding, args: ['@fade',] }], role: [{ type: Input }], text: [{ type: Input }], tooltipHtml: [{ type: Input }], tooltipStyles: [{ type: Input }], arrow: [{ type: Input }], posTop: [{ type: Input }], posLeft: [{ type: Input }] }; class TooltipController { constructor() { this.allowMultiple = true; this.activeTooltips = []; } addTooltip(instance) { if (instance.hideOthers || !this.allowMultiple && this.activeTooltips.length > 0) { this.hideAll(); } this.activeTooltips.push(instance); } removeTooltip(instance) { this.activeTooltips.splice(this.activeTooltips.indexOf(instance), 1); } hideAll() { this.activeTooltips.forEach((tooltip) => { tooltip.removeTooltip(); }); } } TooltipController.ɵprov = ɵɵdefineInjectable({ factory: function TooltipController_Factory() { return new TooltipController(); }, token: TooltipController, providedIn: "root" }); TooltipController.decorators = [ { type: Injectable, args: [{ providedIn: 'root', },] } ]; var TooltipEvent; (function (TooltipEvent) { TooltipEvent["CLICK"] = "click"; TooltipEvent["HOVER"] = "hover"; TooltipEvent["PRESS"] = "press"; })(TooltipEvent || (TooltipEvent = {})); class TooltipDirective { constructor(el, appRef, platform, cfr, tooltipCtrl, vcr) { this.el = el; this.appRef = appRef; this.platform = platform; this.cfr = cfr; this.tooltipCtrl = tooltipCtrl; this.vcr = vcr; this._active = false; this._arrow = false; this._canShow = true; this._debouncedPromise = null; this._navTooltip = false; this.debounce = 0; this.desktopEvent = TooltipEvent.HOVER; this.duration = 3000; this.tooltipStyles = {}; } set navTooltip(val) { this._navTooltip = typeof val !== 'boolean' || val !== false; } get navTooltip() { return this._navTooltip; } set arrow(val) { this._arrow = typeof val !== 'boolean' || val !== false; } get arrow() { return this._arrow; } set active(val) { this._active = typeof val !== 'boolean' || val !== false; this._active && this.canShow ? this.showTooltip() : this.removeTooltip(); } get active() { return this._active; } ngOnInit() { if (typeof this.event === 'undefined') { this.event = this.platform.is('mobile') ? this.mobileEvent : this.desktopEvent; } // if the timer hasn't expired or active is true when the component gets destroyed, the tooltip will remain in the DOM // this removes it this.removeTooltip(); } /** * Show the tooltip immediately after initiating view if set to */ ngAfterViewInit() { if (this._active) { this.trigger(); } } ngOnDestroy() { if (this._tooltipElement && typeof this._tooltipElement.destroy === 'function') { this._tooltipElement.destroy(); } } /** * Set the canShow property * Ensure that tooltip is shown only if the tooltip string is not falsey */ set canShow(show) { this._canShow = show; } /** * @return TRUE if the tooltip can be shown */ get canShow() { return this._canShow && ((typeof this.tooltip === 'string' && this.tooltip !== '') || (typeof this.tooltipHtml === 'string' && this.tooltipHtml !== '')); } /** * Handles the click/press event and shows a tooltip. * If a tooltip already exists, it will just reset it's timer. */ trigger() { if (this.canShow) { if (this._tooltipElement) { this._resetTimer(); } else { this.showTooltip(); } } } /** * Creates a new tooltip component and adjusts it's properties to show properly. */ showTooltip() { this._debouncedPromise = setTimeout(() => { this._debouncedPromise = null; this._createTooltipComponent(); const tooltipComponent = this._tooltipElement.instance; tooltipComponent.role = this.role; tooltipComponent.text = this.tooltip; tooltipComponent.tooltipStyles = this.tooltipStyles; tooltipComponent.tooltipHtml = this.tooltipHtml; tooltipComponent.init.then(() => { const tooltipPosition = this._getTooltipPosition(); tooltipComponent.posLeft = tooltipPosition.left; tooltipComponent.posTop = tooltipPosition.top; tooltipComponent.fadeState = 'visible'; if (this.arrow) { let arrowPosition; if (this.positionV === 'top') { arrowPosition = 'bottom'; } else if (this.positionV === 'bottom') { arrowPosition = 'top'; } else if (this.positionH === 'left') { arrowPosition = 'right'; } else { arrowPosition = 'left'; } tooltipComponent.arrow = arrowPosition; } if (!this._active) { this._tooltipTimeout = setTimeout(this.removeTooltip.bind(this), this.duration); } }); }, this.debounce); } onClick() { if (this.event === TooltipEvent.CLICK) { this.trigger(); } } onPress() { if (this.event === TooltipEvent.PRESS) { this.trigger(); } } onMouseEnter() { if (this.event === TooltipEvent.HOVER) { this.active = true; } } onMouseLeave() { if (this.event === TooltipEvent.HOVER) { this.active = false; } } _createTooltipComponent() { const componentFactory = this.cfr.resolveComponentFactory(TooltipBoxComponent); this._tooltipElement = this.vcr.createComponent(componentFactory); this.tooltipCtrl.addTooltip(this); } _getTooltipPosition() { const tooltipNativeElement = this._tooltipElement.instance.getNativeElement(), el = this.el.nativeElement, rect = el.getBoundingClientRect(); let positionLeft, positionTop, spacing = 10; if (this.navTooltip) { this.positionV = 'bottom'; this.arrow = false; spacing = 20; } if (this.positionH === 'right') { positionLeft = rect.right + spacing; } else if (this.positionH === 'left') { positionLeft = rect.left - spacing - tooltipNativeElement.offsetWidth; // -79, 19 } else if (this.navTooltip) { positionLeft = rect.left + el.offsetWidth / 2; } else { positionLeft = rect.left; } if (this.positionV === 'top') { positionTop = rect.top - spacing - tooltipNativeElement.offsetHeight; } else if (this.positionV === 'bottom') { positionTop = rect.bottom + spacing; } else { positionTop = rect.top + el.offsetHeight / 2 - tooltipNativeElement.offsetHeight / 2; } this.topOffset++; if (this.topOffset) { positionTop += +this.topOffset; } this.leftOffset++; if (this.leftOffset) { positionLeft += +this.leftOffset; } if (positionLeft + tooltipNativeElement.offsetWidth + spacing > this.platform.width()) { positionLeft = this.platform.width() - tooltipNativeElement.offsetWidth - spacing; } else if (positionLeft + tooltipNativeElement.offsetWidth - spacing < 0) { positionLeft = spacing; } if (positionTop + tooltipNativeElement.offsetHeight + spacing > this.platform.height()) { positionTop = this.platform.height() - tooltipNativeElement.offsetHeight - spacing; } else if (positionTop + tooltipNativeElement.offsetHeight - spacing < 0) { positionTop = spacing; } return { left: positionLeft, top: positionTop, }; } removeTooltip() { if (this._debouncedPromise) { clearTimeout(this._debouncedPromise); this._debouncedPromise = null; } if (!this._tooltipElement) { this._tooltipElement = undefined; this._tooltipTimeout = undefined; return; } this._tooltipElement.instance.fadeState = 'invisible'; this.canShow = false; // wait for animation to finish then clear everything out setTimeout(() => { if (this._tooltipElement && typeof this._tooltipElement.destroy === 'function') { this._tooltipElement.destroy(); } this.tooltipCtrl.removeTooltip(this); this._tooltipElement = this._tooltipTimeout = undefined; this.canShow = true; }, 300); } _resetTimer() { clearTimeout(this._tooltipTimeout); this._tooltipTimeout = setTimeout(() => { this.active = false; }, this.duration); } } TooltipDirective.decorators = [ { type: Directive, args: [{ selector: '[tooltip]', },] } ]; TooltipDirective.ctorParameters = () => [ { type: ElementRef }, { type: ApplicationRef }, { type: Platform }, { type: ComponentFactoryResolver }, { type: TooltipController }, { type: ViewContainerRef } ]; TooltipDirective.propDecorators = { debounce: [{ type: Input }], desktopEvent: [{ type: Input }], duration: [{ type: Input }], event: [{ type: Input }], hideOthers: [{ type: Input }], leftOffset: [{ type: Input }], mobileEvent: [{ type: Input }], positionV: [{ type: Input }], positionH: [{ type: Input }], role: [{ type: Input }], tooltip: [{ type: Input }], tooltipHtml: [{ type: Input }], tooltipStyles: [{ type: Input }], topOffset: [{ type: Input }], navTooltip: [{ type: Input }], arrow: [{ type: Input }], active: [{ type: Input }], onClick: [{ type: HostListener, args: ['click',] }], onPress: [{ type: HostListener, args: ['press',] }], onMouseEnter: [{ type: HostListener, args: ['mouseenter',] }], onMouseLeave: [{ type: HostListener, args: ['mouseleave',] }] }; class TooltipsModule { static forRoot() { return { ngModule: TooltipsModule, providers: [ TooltipController ], }; } } TooltipsModule.decorators = [ { type: NgModule, args: [{ declarations: [ TooltipDirective, TooltipBoxComponent ], entryComponents: [ TooltipBoxComponent ], exports: [ TooltipDirective ], imports: [ CommonModule ] },] } ]; /** * Generated bundle index. Do not edit. */ export { TooltipBoxComponent, TooltipController, TooltipDirective, TooltipsModule }; //# sourceMappingURL=ionic4-tooltips.js.map