UNPKG

@ngx-popovers/tooltip

Version:

The Tooltip component based on Angular 17+ and Floating-ui shows tooltips next to the trigger element

353 lines (345 loc) 19.4 kB
import * as i0 from '@angular/core'; import { Input, Component, InjectionToken, inject, EventEmitter, signal, HostListener, Output, ChangeDetectionStrategy, ChangeDetectorRef, input, booleanAttribute, numberAttribute } from '@angular/core'; import { NgxFloatingConfig, offset, flip, FloatingComponent, Arrow, PlatformService } from '@ngx-popovers/core'; import { fromEvent, filter, tap, debounceTime } from 'rxjs'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import { trigger, transition, style, animate } from '@angular/animations'; /** * Default tooltip component. * * It can be replaced with DI */ class TooltipBase { constructor() { this.text = ''; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: TooltipBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: TooltipBase, isStandalone: true, selector: "ng-component", inputs: { text: "text" }, ngImport: i0, template: ` <div class="ngx-tooltip"> {{ text }} </div> `, isInline: true, styles: [".ngx-tooltip{padding:4px 10px;background:#fff;border:1px solid #DDDDDD;border-radius:4px}\n"] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: TooltipBase, decorators: [{ type: Component, args: [{ standalone: true, template: ` <div class="ngx-tooltip"> {{ text }} </div> `, styles: [".ngx-tooltip{padding:4px 10px;background:#fff;border:1px solid #DDDDDD;border-radius:4px}\n"] }] }], propDecorators: { text: [{ type: Input }] } }); class NgxTooltipConfig extends NgxFloatingConfig { constructor(config = {}) { super(); this.debounce = 100; this.arrow = false; this.arrowPadding = 2; this.middleware = [ offset(8), flip() ]; Object.assign(this, config); } } const NGX_TOOLTIP_CONFIG = new InjectionToken('NGX_TOOLTIP_CONFIG', { providedIn: 'root', factory: () => new NgxTooltipConfig() }); const NGX_TOOLTIP_COMPONENT = new InjectionToken('NGX-TOOLTIP_COMPONENT', { providedIn: 'root', factory: () => TooltipBase }); class TooltipTemplate { constructor() { this.text = ''; this.component = inject(NGX_TOOLTIP_COMPONENT); this.hovered = new EventEmitter(); this.isHovered = signal(false); } onMousemove() { if (!this.isHovered()) { this.isHovered.set(true); this.hovered.emit(true); } } onMouseleave() { if (this.isHovered()) { this.isHovered.set(false); this.hovered.emit(false); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: TooltipTemplate, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: TooltipTemplate, isStandalone: true, selector: "ngx-tooltip-template", inputs: { text: "text", component: "component", template: "template" }, outputs: { hovered: "hovered" }, host: { listeners: { "mousemove": "onMousemove($event)", "mouseleave": "onMouseleave($event)" } }, ngImport: i0, template: "@if (template) {\n <ng-template\n [ngTemplateOutlet]=\"template\"\n />\n} @else {\n <ng-template\n [ngComponentOutlet]=\"component\"\n [ngComponentOutletInputs]=\"{text: text}\"\n />\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: TooltipTemplate, decorators: [{ type: Component, args: [{ selector: 'ngx-tooltip-template', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (template) {\n <ng-template\n [ngTemplateOutlet]=\"template\"\n />\n} @else {\n <ng-template\n [ngComponentOutlet]=\"component\"\n [ngComponentOutletInputs]=\"{text: text}\"\n />\n}\n" }] }], propDecorators: { text: [{ type: Input }], component: [{ type: Input }], template: [{ type: Input }], hovered: [{ type: Output }], onMousemove: [{ type: HostListener, args: ['mousemove', ['$event']] }], onMouseleave: [{ type: HostListener, args: ['mouseleave', ['$event']] }] } }); class NgxTooltip { set ngxValue(value) { this._ngxValue = value; if (value) { this.onShow(); } else { this.onHide(); } } get ngxValue() { return this._ngxValue; } get reference() { return this.el.nativeElement; } get fixedDebounce() { return this.fixDebounce(this.debounce); } constructor(el) { this.el = el; this.config = inject(NGX_TOOLTIP_CONFIG); this.cdRef = inject(ChangeDetectorRef); this.tooltipText = ''; this.tooltipComponent = inject(NGX_TOOLTIP_COMPONENT); this.placement = this.config.placement; this.middleware = this.config.middleware; this.strategy = input(this.config.strategy); this._ngxValue = false; this.ngxValueChange = new EventEmitter(); /** * Time delay before the tooltip is displayed */ this.debounce = this.config.debounce; /** * Show arrow or not */ this.arrow = this.config.arrow; /** * Show arrow or not */ this.arrowPadding = this.config.arrowPadding; /** * Updates floating element automatically */ this.autoUpdate = this.config.autoUpdate; /** * HTMLElement where floating renders */ this.bindTo = this.config.bindTo; this.tooltipDisabled = false; this.animationDisabled = false; /** * Emits when tooltip shows */ this.showEnd = new EventEmitter(); /** * Emits when tooltip hides */ this.hideEnd = new EventEmitter(); this.animationStart = new EventEmitter(); this.animationDone = new EventEmitter(); this.clickedOutside = new EventEmitter(); this.clickedInside = new EventEmitter(); this.computePosition = new EventEmitter(); this.isTooltipCreated = signal(false); this.isReferenceHovered = signal(false); this.isTooltipHovered = signal(false); this.isAnimating = signal(false); } ngOnChanges() { this.initListeners(); } initListeners() { this.mouseMoveListener(); this.mouseleaveListener(); this.focusListener(); this.blurListener(); } mouseMoveListener() { this.moveSubscriber$?.unsubscribe(); this.moveSubscriber$ = fromEvent(this.reference, 'mousemove').pipe(filter(() => !this.tooltipDisabled), tap(() => this.isReferenceHovered.set(true)), filter(() => !this.isTooltipCreated()), debounceTime(this.fixedDebounce), filter(() => this.isReferenceHovered())).subscribe(() => { this.ngxValue = true; this.cdRef.detectChanges(); }); } focusListener() { this.focusListener$?.unsubscribe(); this.focusListener$ = fromEvent(this.reference, 'focus').pipe(filter(() => !this.tooltipDisabled), filter(() => !this.isTooltipCreated()), debounceTime(this.fixedDebounce)).subscribe(() => { this.ngxValue = true; }); } mouseleaveListener() { this.leaveSubscriber$?.unsubscribe(); this.leaveSubscriber$ = fromEvent(this.reference, 'mouseleave').pipe(filter(() => !this.tooltipDisabled), tap(() => this.isReferenceHovered.set(false)), filter(() => this.isTooltipCreated()), debounceTime(this.fixedDebounce), filter(() => !this.isTooltipHovered())).subscribe(() => { this.ngxValue = false; this.cdRef.detectChanges(); }); } blurListener() { this.blurListener$?.unsubscribe(); this.blurListener$ = fromEvent(this.reference, 'blur').pipe(filter(() => !this.tooltipDisabled), filter(() => this.isTooltipCreated()), debounceTime(this.fixedDebounce), filter(() => !this.isTooltipHovered())).subscribe(() => { this.ngxValue = false; }); } onShow() { if (this.isTooltipCreated()) { return; } this.show(); } onHide() { if (!this.isTooltipCreated()) { return; } this.hide(); } show() { this.isAnimating.set(true); this.isTooltipCreated.set(true); this.showEnd.emit(); this.ngxValueChange.emit(true); } hide() { this.isAnimating.set(true); this.isTooltipCreated.set(false); this.hideEnd.emit(); this.ngxValueChange.emit(false); } setTooltipHovered($event) { if (!$event) { this.hide(); } this.isTooltipHovered.set($event); } onClickedInside(element) { this.clickedInside.emit(element); } /** * Check if user clicked outside the reference and the tooltip element. * Close tooltip if user clicks outside (actually for touch devices) */ onClickedOutside(element) { if (element !== this.reference) { this.ngxValue = false; } this.clickedOutside.emit(element); } onAnimationStart(event) { this.isAnimating.set(true); this.animationStart.emit(event); } onAnimationDone(event) { this.isAnimating.set(false); this.animationDone.emit(event); } onPositionReturn($event) { this.computePosition.emit($event); } fixDebounce(time) { if (typeof time === 'number') { return time >= 0 ? time : 0; } return 0; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgxTooltip, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: NgxTooltip, isStandalone: true, selector: "[ngxTooltip]", inputs: { tooltipText: { classPropertyName: "tooltipText", publicName: "ngxTooltip", isSignal: false, isRequired: false, transformFunction: null }, tooltipComponent: { classPropertyName: "tooltipComponent", publicName: "tooltipComponent", isSignal: false, isRequired: false, transformFunction: null }, template: { classPropertyName: "template", publicName: "template", isSignal: false, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: false, isRequired: false, transformFunction: null }, middleware: { classPropertyName: "middleware", publicName: "middleware", isSignal: false, isRequired: false, transformFunction: null }, strategy: { classPropertyName: "strategy", publicName: "strategy", isSignal: true, isRequired: false, transformFunction: null }, ngxValue: { classPropertyName: "ngxValue", publicName: "ngxValue", isSignal: false, isRequired: false, transformFunction: null }, debounce: { classPropertyName: "debounce", publicName: "debounce", isSignal: false, isRequired: false, transformFunction: numberAttribute }, arrow: { classPropertyName: "arrow", publicName: "arrow", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, arrowPadding: { classPropertyName: "arrowPadding", publicName: "arrowPadding", isSignal: false, isRequired: false, transformFunction: numberAttribute }, autoUpdate: { classPropertyName: "autoUpdate", publicName: "autoUpdate", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, bindTo: { classPropertyName: "bindTo", publicName: "bindTo", isSignal: false, isRequired: false, transformFunction: null }, tooltipDisabled: { classPropertyName: "tooltipDisabled", publicName: "tooltipDisabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, animationDisabled: { classPropertyName: "animationDisabled", publicName: "animationDisabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute } }, outputs: { ngxValueChange: "ngxValueChange", showEnd: "showEnd", hideEnd: "hideEnd", animationStart: "animationStart", animationDone: "animationDone", clickedOutside: "clickedOutside", clickedInside: "clickedInside", computePosition: "computePosition" }, providers: [PlatformService], usesOnChanges: true, ngImport: i0, template: "<ng-content />\n\n@if ((isTooltipCreated() || isAnimating()) && reference.isConnected) {\n <ngx-floating\n [reference]=\"reference\"\n [placement]=\"placement\"\n [middleware]=\"middleware\"\n [autoUpdate]=\"autoUpdate\"\n [bindTo]=\"bindTo\"\n [strategy]=\"strategy()\"\n\n (clickedInside)=\"onClickedInside($event)\"\n (clickedOutside)=\"onClickedOutside($event)\"\n (computePositionReturn)=\"onPositionReturn($event)\"\n >\n @if (isTooltipCreated()) {\n <div\n @fadeInOut\n (@fadeInOut.start)=\"onAnimationStart($event)\"\n (@fadeInOut.done)=\"onAnimationDone($event)\"\n [@.disabled]=\"animationDisabled\"\n >\n <ngx-tooltip-template\n [template]=\"template\"\n [component]=\"tooltipComponent\"\n [text]=\"tooltipText\"\n (hovered)=\"setTooltipHovered($event)\"\n />\n\n @if (arrow) {\n <ngx-arrow [padding]=\"arrowPadding\" />\n }\n </div>\n }\n </ngx-floating>\n}\n", styles: [""], dependencies: [{ kind: "component", type: TooltipTemplate, selector: "ngx-tooltip-template", inputs: ["text", "component", "template"], outputs: ["hovered"] }, { kind: "component", type: FloatingComponent, selector: "ngx-floating", inputs: ["trigger", "reference", "placement", "strategy", "autoUpdate", "arrow", "middleware", "bindTo"], outputs: ["arrowChange", "clickedOutside", "clickedInside", "computePositionReturn"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: Arrow, selector: "ngx-arrow", inputs: ["floating", "padding"], outputs: ["floatingChange"] }], animations: [ trigger('fadeInOut', [ transition(':enter', [ style({ opacity: 0, transform: 'scale(0.9)' }), animate(140, style({ opacity: 1, transform: 'scale(1)' })) ]), transition(':leave', [ animate(100, style({ opacity: 0 })) ]) ]) ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgxTooltip, decorators: [{ type: Component, args: [{ selector: '[ngxTooltip]', imports: [ TooltipTemplate, FloatingComponent, CommonModule, Arrow ], providers: [PlatformService], changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger('fadeInOut', [ transition(':enter', [ style({ opacity: 0, transform: 'scale(0.9)' }), animate(140, style({ opacity: 1, transform: 'scale(1)' })) ]), transition(':leave', [ animate(100, style({ opacity: 0 })) ]) ]) ], template: "<ng-content />\n\n@if ((isTooltipCreated() || isAnimating()) && reference.isConnected) {\n <ngx-floating\n [reference]=\"reference\"\n [placement]=\"placement\"\n [middleware]=\"middleware\"\n [autoUpdate]=\"autoUpdate\"\n [bindTo]=\"bindTo\"\n [strategy]=\"strategy()\"\n\n (clickedInside)=\"onClickedInside($event)\"\n (clickedOutside)=\"onClickedOutside($event)\"\n (computePositionReturn)=\"onPositionReturn($event)\"\n >\n @if (isTooltipCreated()) {\n <div\n @fadeInOut\n (@fadeInOut.start)=\"onAnimationStart($event)\"\n (@fadeInOut.done)=\"onAnimationDone($event)\"\n [@.disabled]=\"animationDisabled\"\n >\n <ngx-tooltip-template\n [template]=\"template\"\n [component]=\"tooltipComponent\"\n [text]=\"tooltipText\"\n (hovered)=\"setTooltipHovered($event)\"\n />\n\n @if (arrow) {\n <ngx-arrow [padding]=\"arrowPadding\" />\n }\n </div>\n }\n </ngx-floating>\n}\n" }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { tooltipText: [{ type: Input, args: ['ngxTooltip'] }], tooltipComponent: [{ type: Input }], template: [{ type: Input }], placement: [{ type: Input }], middleware: [{ type: Input }], ngxValue: [{ type: Input }], ngxValueChange: [{ type: Output }], debounce: [{ type: Input, args: [{ transform: numberAttribute }] }], arrow: [{ type: Input, args: [{ transform: booleanAttribute }] }], arrowPadding: [{ type: Input, args: [{ transform: numberAttribute }] }], autoUpdate: [{ type: Input, args: [{ transform: booleanAttribute }] }], bindTo: [{ type: Input }], tooltipDisabled: [{ type: Input, args: [{ transform: booleanAttribute }] }], animationDisabled: [{ type: Input, args: [{ transform: booleanAttribute }] }], showEnd: [{ type: Output }], hideEnd: [{ type: Output }], animationStart: [{ type: Output }], animationDone: [{ type: Output }], clickedOutside: [{ type: Output }], clickedInside: [{ type: Output }], computePosition: [{ type: Output }] } }); /** * Generated bundle index. Do not edit. */ export { NGX_TOOLTIP_COMPONENT, NGX_TOOLTIP_CONFIG, NgxTooltip, NgxTooltipConfig, TooltipBase }; //# sourceMappingURL=ngx-popovers-tooltip.mjs.map