ngx-ellipsis-tool
Version:
Angular directive that automatically detects when text is truncated with ellipsis (...) and shows a tooltip with the full text — only when needed!
164 lines (159 loc) • 7.06 kB
JavaScript
import * as i0 from '@angular/core';
import { Input, ChangeDetectionStrategy, Component, inject, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent, HostListener, Directive } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
class TooltipComponent {
constructor() {
this.text = '';
this.top = 0;
this.left = 0;
this.customClass = '';
}
get formattedText() {
return this.text.replace(/\n/g, '<br>');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: TooltipComponent, isStandalone: true, selector: "app-tooltip", inputs: { text: "text", top: "top", left: "left", customClass: "customClass" }, ngImport: i0, template: `
<div
class="custom-tooltip"
[ngClass]="customClass"
[style.top.px]="top"
[style.left.px]="left"
>
<span [innerHTML]="formattedText"></span>
</div>
`, isInline: true, styles: [".custom-tooltip{background:#333;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;position:absolute;white-space:pre-line;z-index:10000;pointer-events:none;box-shadow:0 2px 8px #0000004d;transform:translateY(-100%);max-width:300px;word-wrap:break-word}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TooltipComponent, decorators: [{
type: Component,
args: [{ selector: 'app-tooltip', standalone: true, imports: [CommonModule], template: `
<div
class="custom-tooltip"
[ngClass]="customClass"
[style.top.px]="top"
[style.left.px]="left"
>
<span [innerHTML]="formattedText"></span>
</div>
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".custom-tooltip{background:#333;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;position:absolute;white-space:pre-line;z-index:10000;pointer-events:none;box-shadow:0 2px 8px #0000004d;transform:translateY(-100%);max-width:300px;word-wrap:break-word}\n"] }]
}], propDecorators: { text: [{
type: Input
}], top: [{
type: Input
}], left: [{
type: Input
}], customClass: [{
type: Input
}] } });
class NgxEllipsisTooltipDirective {
constructor(el) {
this.el = el;
this.placement = 'top';
this.delay = 300;
this.tooltipClass = '';
this.appRef = inject(ApplicationRef);
this.envInjector = inject(EnvironmentInjector);
this.destroyRef = inject(DestroyRef);
this.destroyRef.onDestroy(() => this.hideTooltip());
}
onMouseEnter() {
this.scheduleShowTooltip();
}
onMouseLeave() {
this.cancelShow();
this.hideTooltip();
}
onClick() {
// For mobile → show immediately on click
this.cancelShow();
this.tryShowTooltip();
}
onDocumentClick(event) {
if (this.tooltipRef && !this.el.nativeElement.contains(event.target)) {
this.hideTooltip();
}
}
scheduleShowTooltip() {
this.cancelShow();
this.showTimeoutId = setTimeout(() => {
this.tryShowTooltip();
}, this.delay);
}
cancelShow() {
if (this.showTimeoutId) {
clearTimeout(this.showTimeoutId);
this.showTimeoutId = null;
}
}
tryShowTooltip() {
if (this.isTruncated()) {
const text = this.tooltipText || this.el.nativeElement.textContent?.trim() || '';
const rect = this.el.nativeElement.getBoundingClientRect();
let top = rect.top + window.scrollY;
let left = rect.left + window.scrollX;
const elHeight = rect.height;
if (this.placement === 'top') {
top -= 8;
}
else {
top += elHeight + 8;
}
this.showTooltip(text, left, top);
}
}
isTruncated() {
const el = this.el.nativeElement;
return el.scrollWidth > el.clientWidth || el.scrollHeight > el.clientHeight;
}
showTooltip(text, left, top) {
this.hideTooltip();
this.tooltipRef = createComponent(TooltipComponent, {
environmentInjector: this.envInjector,
hostElement: document.body
});
this.tooltipRef.instance.text = text;
this.tooltipRef.instance.left = left;
this.tooltipRef.instance.top = top;
this.tooltipRef.instance.customClass = this.tooltipClass;
}
hideTooltip() {
if (this.tooltipRef) {
this.tooltipRef.destroy();
this.tooltipRef = undefined;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NgxEllipsisTooltipDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NgxEllipsisTooltipDirective, isStandalone: true, selector: "[ngxEllipsisTooltip]", inputs: { tooltipText: "tooltipText", placement: "placement", delay: "delay", tooltipClass: "tooltipClass" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "click": "onClick()", "document:click": "onDocumentClick($event)" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NgxEllipsisTooltipDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxEllipsisTooltip]',
standalone: true
}]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { tooltipText: [{
type: Input
}], placement: [{
type: Input
}], delay: [{
type: Input
}], tooltipClass: [{
type: Input
}], onMouseEnter: [{
type: HostListener,
args: ['mouseenter']
}], onMouseLeave: [{
type: HostListener,
args: ['mouseleave']
}], onClick: [{
type: HostListener,
args: ['click']
}], onDocumentClick: [{
type: HostListener,
args: ['document:click', ['$event']]
}] } });
/**
* Generated bundle index. Do not edit.
*/
export { NgxEllipsisTooltipDirective };
//# sourceMappingURL=ngx-ellipsis-tool.mjs.map