@duyvu-fsdev/ng-tooltip-directive
Version:
Tooltip directive designed for Angular and Ionic/Angular applications.
287 lines (280 loc) • 18.7 kB
JavaScript
import * as i1 from '@angular/common';
import { DOCUMENT, CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Component, Input, Directive, Inject, HostListener, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
class TooltipComponent {
position = "bottom";
top;
left;
visible = false;
text = "";
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.12", type: TooltipComponent, selector: "ng-tooltip", inputs: { position: "position", top: "top", left: "left", visible: "visible", text: "text" }, ngImport: i0, template: "<div\n class=\"app-tooltip tooltip-container {{ position }}\"\n [ngStyle]=\"{\n top: top,\n left: left,\n visibility: visible ? 'visible' : 'hidden'\n }\"\n>\n <div class=\"tooltip-arrow\"></div>\n <div class=\"tooltip-content\">{{ text }}</div>\n</div>\n", styles: [".app-tooltip{--background-color: var(--tooltip-background, #26292c);--color: var(--tooltip-color, #f4f5f8);--max-width: var(--tooltip-max-width, fit-content);--height: var(--tooltip-height, fit-content);--border-radius: var(--tooltip-border-radius, 4px);--padding: var(--tooltip-padding, 6px 8px)}.app-tooltip *{box-sizing:border-box}.tooltip-container{position:fixed;background-color:var(--background-color);color:var(--color);padding:var(--padding);border-radius:var(--border-radius);z-index:1000;max-width:var(--max-width);height:var(--height)}.tooltip-container .tooltip-content{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.tooltip-container .tooltip-arrow{position:absolute;width:0;height:0}.tooltip-container.top{transform:translate(-50%,-100%)}.tooltip-container.top .tooltip-arrow{bottom:-5px;left:50%;transform:translate(-50%);border-width:5px 5px 0;border-style:solid;border-color:var(--background-color) transparent transparent transparent}.tooltip-container.bottom{transform:translate(-50%)}.tooltip-container.bottom .tooltip-arrow{top:-5px;left:50%;transform:translate(-50%);border-width:0 5px 5px;border-style:solid;border-color:transparent transparent var(--background-color) transparent}.tooltip-container.left{transform:translate(-100%,-50%)}.tooltip-container.left .tooltip-arrow{top:50%;right:-5px;transform:translateY(-50%);border-width:5px 0 5px 5px;border-style:solid;border-color:transparent transparent transparent var(--background-color)}.tooltip-container.right{transform:translateY(-50%)}.tooltip-container.right .tooltip-arrow{top:50%;left:-5px;transform:translateY(-50%);border-width:5px 5px 5px 0;border-style:solid;border-color:transparent var(--background-color) transparent transparent}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipComponent, decorators: [{
type: Component,
args: [{ selector: "ng-tooltip", template: "<div\n class=\"app-tooltip tooltip-container {{ position }}\"\n [ngStyle]=\"{\n top: top,\n left: left,\n visibility: visible ? 'visible' : 'hidden'\n }\"\n>\n <div class=\"tooltip-arrow\"></div>\n <div class=\"tooltip-content\">{{ text }}</div>\n</div>\n", styles: [".app-tooltip{--background-color: var(--tooltip-background, #26292c);--color: var(--tooltip-color, #f4f5f8);--max-width: var(--tooltip-max-width, fit-content);--height: var(--tooltip-height, fit-content);--border-radius: var(--tooltip-border-radius, 4px);--padding: var(--tooltip-padding, 6px 8px)}.app-tooltip *{box-sizing:border-box}.tooltip-container{position:fixed;background-color:var(--background-color);color:var(--color);padding:var(--padding);border-radius:var(--border-radius);z-index:1000;max-width:var(--max-width);height:var(--height)}.tooltip-container .tooltip-content{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.tooltip-container .tooltip-arrow{position:absolute;width:0;height:0}.tooltip-container.top{transform:translate(-50%,-100%)}.tooltip-container.top .tooltip-arrow{bottom:-5px;left:50%;transform:translate(-50%);border-width:5px 5px 0;border-style:solid;border-color:var(--background-color) transparent transparent transparent}.tooltip-container.bottom{transform:translate(-50%)}.tooltip-container.bottom .tooltip-arrow{top:-5px;left:50%;transform:translate(-50%);border-width:0 5px 5px;border-style:solid;border-color:transparent transparent var(--background-color) transparent}.tooltip-container.left{transform:translate(-100%,-50%)}.tooltip-container.left .tooltip-arrow{top:50%;right:-5px;transform:translateY(-50%);border-width:5px 0 5px 5px;border-style:solid;border-color:transparent transparent transparent var(--background-color)}.tooltip-container.right{transform:translateY(-50%)}.tooltip-container.right .tooltip-arrow{top:50%;left:-5px;transform:translateY(-50%);border-width:5px 5px 5px 0;border-style:solid;border-color:transparent var(--background-color) transparent transparent}\n"] }]
}], propDecorators: { position: [{
type: Input
}], top: [{
type: Input
}], left: [{
type: Input
}], visible: [{
type: Input
}], text: [{
type: Input
}] } });
class TooltipDirective {
el;
viewContainerRef;
document;
option;
tooltipComponentRef;
constructor(el, viewContainerRef, document) {
this.el = el;
this.viewContainerRef = viewContainerRef;
this.document = document;
}
ngAfterViewInit() {
this.option.position = this.option.position ?? "bottom";
this.option.class = this.option.class ?? "";
}
onMouseEnter() {
this.showTooltip();
}
onMouseLeave() {
this.hideTooltip();
}
getHostPosition(hostEl) {
if (!hostEl)
return { h: 0, w: 0, l: 0, t: 0 };
const styles = getComputedStyle(hostEl);
const { left, top, height, width } = hostEl.getBoundingClientRect();
const l = styles.left === "auto" ? left : left + parseFloat(styles.marginLeft);
const t = styles.top === "auto" ? top : top + parseFloat(styles.marginTop);
const w = styles.width === "auto" ? width : parseFloat(styles.width);
const h = styles.height === "auto" ? height : parseFloat(styles.height);
return { w, h, l, t };
}
getStandardPosition(p) {
const { h, w, l, t } = this.getHostPosition(this.el.nativeElement);
if (p === "top")
return { left: l + w / 2, top: t - 6 };
if (p === "left")
return { left: l - 6, top: t + h / 2 };
if (p === "right")
return { left: l + w + 6, top: t + h / 2 };
else
return { left: l + w / 2, top: t + h + 6 };
}
showTooltip() {
const factory = this.viewContainerRef.createComponent(TooltipComponent);
this.tooltipComponentRef = factory;
const tooltipInstance = this.tooltipComponentRef.instance;
const tooltipEl = this.tooltipComponentRef.location.nativeElement;
this.document.body.appendChild(tooltipEl);
const { left, top } = this.getStandardPosition(this.option.position ?? "bottom");
tooltipInstance.position = this.option.position ?? "bottom";
tooltipInstance.text = this.option.text;
tooltipInstance.left = `${left}px`;
tooltipInstance.top = `${top}px`;
tooltipInstance.visible = false;
setTimeout(() => {
const tooltipEl = this.tooltipComponentRef.location.nativeElement.querySelector(".tooltip-container");
if (this.option.class)
tooltipEl.classList.add(this.option.class);
this.adjustPosition(tooltipEl).then((position) => {
const { left, top } = this.getStandardPosition(position);
tooltipInstance.position = position;
tooltipInstance.left = `${left}px`;
tooltipInstance.top = `${top}px`;
tooltipInstance.visible = true;
});
}, 1);
}
adjustPosition(initTooltipEl) {
return new Promise((resolve) => {
const { left, right, top, bottom } = initTooltipEl.getBoundingClientRect();
const windowWidth = document.documentElement.clientWidth;
const windowHeight = document.documentElement.clientHeight;
const isOverLeft = left < 0;
const isOverRight = right > windowWidth;
const isOverTop = top < 0;
const isOverBottom = bottom > windowHeight;
if (!isOverLeft && !isOverRight && !isOverBottom && !isOverTop)
resolve(this.option.position ?? "bottom");
else if (isOverLeft && isOverRight && isOverBottom && isOverTop)
resolve("bottom");
else if (this.option.position === "top") {
if (isOverLeft && !isOverRight && !isOverBottom && !isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("top");
if (!isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("bottom");
if (isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("top");
if (isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("right");
if (isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("left");
if (isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("top");
if (isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && isOverTop)
resolve("left");
}
else if (this.option.position === "left") {
if (isOverLeft && !isOverRight && !isOverBottom && !isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("left");
if (isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("right");
if (isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("left");
if (isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("top");
if (isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && isOverTop)
resolve("left");
}
else if (this.option.position === "right") {
if (isOverLeft && !isOverRight && !isOverBottom && !isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("right");
if (!isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("right");
if (isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("right");
if (isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("right");
if (isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("top");
if (isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && isOverTop)
resolve("left");
}
else if (this.option.position === "bottom") {
if (isOverLeft && !isOverRight && !isOverBottom && !isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("top");
if (!isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("bottom");
if (isOverLeft && isOverRight && !isOverBottom && !isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && !isOverTop)
resolve("right");
if (isOverLeft && !isOverRight && !isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("left");
if (!isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("right");
if (isOverLeft && isOverRight && isOverBottom && !isOverTop)
resolve("top");
if (isOverLeft && isOverRight && !isOverBottom && isOverTop)
resolve("bottom");
if (isOverLeft && !isOverRight && isOverBottom && isOverTop)
resolve("right");
if (!isOverLeft && isOverRight && isOverBottom && isOverTop)
resolve("left");
}
});
}
hideTooltip() {
if (this.tooltipComponentRef) {
const tooltipInstance = this.tooltipComponentRef.instance;
tooltipInstance.visible = false;
this.tooltipComponentRef.destroy();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipDirective, deps: [{ token: i0.ElementRef }, { token: i0.ViewContainerRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.12", type: TooltipDirective, selector: "[tooltip]", inputs: { option: ["tooltipOption", "option"] }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipDirective, decorators: [{
type: Directive,
args: [{
selector: "[tooltip]",
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ViewContainerRef }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }], propDecorators: { option: [{
type: Input,
args: ["tooltipOption"]
}], onMouseEnter: [{
type: HostListener,
args: ["mouseenter"]
}], onMouseLeave: [{
type: HostListener,
args: ["mouseleave"]
}] } });
class TooltipModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.12", ngImport: i0, type: TooltipModule, declarations: [TooltipComponent, TooltipDirective], imports: [CommonModule, FormsModule, ReactiveFormsModule], exports: [TooltipDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipModule, imports: [CommonModule, FormsModule, ReactiveFormsModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TooltipModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, FormsModule, ReactiveFormsModule],
declarations: [TooltipComponent, TooltipDirective],
exports: [TooltipDirective],
}]
}] });
/*
* Public API Surface of ng-tooltip-directive
*/
/**
* Generated bundle index. Do not edit.
*/
export { TooltipComponent, TooltipDirective, TooltipModule };
//# sourceMappingURL=duyvu-fsdev-ng-tooltip-directive.mjs.map