Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use custom component as tooltip in Angular

Is it possible to use a custom component as tooltip in Angular 8?
Without styling directly the mat-tooltip I'm wondering if one can use a custom component to show as tooltip when using the directive. Like "when showing tooltip, show my component".
Can it be done?

like image 569
User978438 Avatar asked Sep 18 '25 17:09

User978438


1 Answers

Yes, You can create your custom tooltip directive.

tooltip.directive.ts

import { Directive, Input, ElementRef, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[tooltip]'
})
export class TooltipDirective {
  @Input('tooltip') tooltipTitle: string;
  @Input() placement: string;
  @Input() delay: string;
  tooltip: HTMLElement;
  offset = 10;

  constructor(private el: ElementRef, private renderer: Renderer2) { }

  @HostListener('mouseenter') onMouseEnter() {
    if (!this.tooltip) { this.show(); }
  }

  @HostListener('mouseleave') onMouseLeave() {
    if (this.tooltip) { this.hide(); }
  }

  show() {
    this.create();
    this.setPosition();
    this.renderer.addClass(this.tooltip, 'ng-tooltip-show');
  }

  hide() {
    this.renderer.removeClass(this.tooltip, 'ng-tooltip-show');
    window.setTimeout(() => {
      this.renderer.removeChild(document.body, this.tooltip);
      this.tooltip = null;
    }, this.delay);
  }

  create() {
    this.tooltip = this.renderer.createElement('span');

    this.renderer.appendChild(
      this.tooltip,
      this.renderer.createText(this.tooltipTitle) // Here is your text
    );

    this.renderer.appendChild(document.body, this.tooltip);

    this.renderer.addClass(this.tooltip, 'ng-tooltip');
    this.renderer.addClass(this.tooltip, `ng-tooltip-${this.placement}`);
    this.renderer.setStyle(this.tooltip, '-webkit-transition', `opacity ${this.delay}ms`);
    this.renderer.setStyle(this.tooltip, '-moz-transition', `opacity ${this.delay}ms`);
    this.renderer.setStyle(this.tooltip, '-o-transition', `opacity ${this.delay}ms`);
    this.renderer.setStyle(this.tooltip, 'transition', `opacity ${this.delay}ms`);
  }

  setPosition() {
    const hostPos = this.el.nativeElement.getBoundingClientRect();
    const tooltipPos = this.tooltip.getBoundingClientRect();

    const scrollPos = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

    let top, left;

    if (this.placement === 'top') {
      top = hostPos.top - tooltipPos.height - this.offset;
      left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
    }

    if (this.placement === 'bottom') {
      top = hostPos.bottom + this.offset;
      left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
    }

    if (this.placement === 'left') {
      top = hostPos.top + (hostPos.height - tooltipPos.height) / 2;
      left = hostPos.left - tooltipPos.width - this.offset;
    }

    if (this.placement === 'right') {
      top = hostPos.top + (hostPos.height - tooltipPos.height) / 2;
      left = hostPos.right + this.offset;
    }

    this.renderer.setStyle(this.tooltip, 'top', `${top + scrollPos}px`);
    this.renderer.setStyle(this.tooltip, 'left', `${left}px`);
  }
}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div class="tooltip-example">
      <div tooltip="left description" placement="left" delay="500">tootip on left</div>
      <div tooltip="top description" placement="top" delay="500">tootip on top</div>
      <div tooltip="bottom description" placement="bottom" delay="500">tootip on bottom</div>
      <div tooltip="right description" placement="right" delay="500">tootip on right</div>
    </div>
  `,
  styles: [`
    .tooltip-example {
      text-align: center;
      padding: 0 50px;
    }
    .tooltip-example [tooltip] {
      display: inline-block;
      margin: 50px 20px;
      width: 180px;
      height: 50px;
      border: 1px solid gray;
      border-radius: 5px;
      line-height: 50px;
      text-align: center;
    }
    .ng-tooltip {
      position: absolute;
      max-width: 150px;
      font-size: 14px;
      text-align: center;
      color: #f8f8f2;
      padding: 3px 8px;
      background: #282a36;
      border-radius: 4px;
      z-index: 1000;
      opacity: 0;
    }
    .ng-tooltip:after {
      content: "";
      position: absolute;
      border-style: solid;
    }
    .ng-tooltip-top:after {
      top: 100%;
      left: 50%;
      margin-left: -5px;
      border-width: 5px;
      border-color: black transparent transparent transparent;
    }
    .ng-tooltip-bottom:after {
      bottom: 100%;
      left: 50%;
      margin-left: -5px;
      border-width: 5px;
      border-color: transparent transparent black transparent;
    }
    .ng-tooltip-left:after {
      top: 50%;
      left: 100%;
      margin-top: -5px;
      border-width: 5px;
      border-color: transparent transparent transparent black;
    }
    .ng-tooltip-right:after {
      top: 50%;
      right: 100%;
      margin-top: -5px;
      border-width: 5px;
      border-color: transparent black transparent transparent;
    }
    .ng-tooltip-show {
      opacity: 1;
    }
  `]
})
export class AppComponent {}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { TooltipDirective } from './tooltip.directive';

@NgModule({
  declarations: [
    AppComponent,
    TooltipDirective
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
like image 50
Mehul Rudatala Avatar answered Sep 23 '25 09:09

Mehul Rudatala