@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
JavaScript
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