Skip to content
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
All notable changes for each version of this project will be documented in this file.


## 20.2.0

### New Features

- `IgxTooltipTarget`
- Added new properties:
- `showTriggers` - Which event triggers will show the tooltip. Expects a comma-separated string of different event triggers. Defaults to `pointerenter`.
- `hideTriggers` - Which event triggers will hide the tooltip. Expects a comma-separated string of different event triggers. Defaults to `pointerleave` and `click`.

```html
<igx-icon [igxTooltipTarget]="tooltipRef" [showTriggers]="'click,focus'" [hideTriggers]="'keypress,blur'">info</igx-icon>
<span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
```

## 20.1.0

### New Features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ Since the **IgxTooltip** directive extends the **IgxToggle** directive and there
| hasArrow | boolean | Controls whether to display an arrow indicator for the tooltip. Defaults to `false`. |
| sticky | boolean | When set to `true`, the tooltip renders a default close icon `x`. The tooltip remains visible until the user closes it via the close icon `x` or `Esc` key. Defaults to `false`. |
| closeButtonTemplate | TemplateRef<any> | Allows templating the default close icon `x`. |
| showTriggers | string | Which event triggers will show the tooltip. Expects a comma-separated string of different event triggers. Defaults to `pointerenter`. |
| hideTriggers | string | Which event triggers will hide the tooltip. Expects a comma-separated string of different event triggers. Defaults to `pointerleave` and `click`. |


> Note: Setting `showTriggers` and `hideTriggers` only has effect when interacting with the target, not the tooltip itself. Default event triggers for the tooltip are `pointerenter` and `pointerleave`.

#### Templating the close button

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
Directive, OnInit, OnDestroy, Output, ElementRef, Optional, ViewContainerRef, HostListener,
Directive, OnInit, OnDestroy, Output, ElementRef, ViewContainerRef,
Input, EventEmitter, booleanAttribute, TemplateRef, ComponentRef, Renderer2,
EnvironmentInjector,
createComponent,
AfterViewInit,
inject,
} from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
Expand All @@ -14,7 +15,7 @@ import { IgxToggleActionDirective } from '../toggle/toggle.directive';
import { IgxTooltipComponent } from './tooltip.component';
import { IgxTooltipDirective } from './tooltip.directive';
import { IgxTooltipCloseButtonComponent } from './tooltip-close-button.component';
import { TooltipPositionSettings, TooltipPositionStrategy } from './tooltip.common';
import { parseTriggers, TooltipPositionSettings, TooltipPositionStrategy } from './tooltip.common';

export interface ITooltipShowEventArgs extends IBaseEventArgs {
target: IgxTooltipTargetDirective;
Expand Down Expand Up @@ -232,6 +233,46 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
@Input({ transform: booleanAttribute })
public tooltipDisabled = false;

/**
* Which event triggers will show the tooltip.
* Expects a comma-separated string of different event triggers.
* Defaults to `pointerenter`.
* ```html
* <igx-icon [igxTooltipTarget]="tooltipRef" [showTriggers]="'click,focus'">info</igx-icon>
* <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
* ```
*/
@Input()
public get showTriggers(): string {
return Array.from(this._showTriggers).join();
}

public set showTriggers(value: string) {
this._showTriggers = parseTriggers(value);
this.removeEventListeners();
this.addEventListeners();
}

/**
* Which event triggers will hide the tooltip.
* Expects a comma-separated string of different event triggers.
* Defaults to `pointerleave` and `click`.
* ```html
* <igx-icon [igxTooltipTarget]="tooltipRef" [hideTriggers]="'keypress,blur'">info</igx-icon>
* <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
* ```
*/
@Input()
public get hideTriggers(): string {
return Array.from(this._hideTriggers).join();
}

public set hideTriggers(value: string) {
this._hideTriggers = parseTriggers(value);
this.removeEventListeners();
this.addEventListeners();
}

/**
* @hidden
*/
Expand All @@ -253,8 +294,11 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
}

/**
* @hidden
*/
* Specifies a plain text as tooltip content.
* ```html
* <igx-icon igxTooltipTarget [tooltip]="'Infragistics Inc. HQ'">info</igx-icon>
* ```
*/
@Input()
public set tooltip(content: any) {
if (!this.target && (typeof content === 'string' || content instanceof String)) {
Expand Down Expand Up @@ -323,6 +367,12 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
@Output()
public tooltipHide = new EventEmitter<ITooltipHideEventArgs>();

private _element = inject(ElementRef);
private _navigationService = inject(IgxNavigationService, { optional: true });
private _viewContainerRef = inject(ViewContainerRef);
private _renderer = inject(Renderer2);
private _envInjector = inject(EnvironmentInjector);

private _destroy$ = new Subject<void>();
private _autoHideDelay = 180;
private _isForceClosed = false;
Expand All @@ -331,25 +381,20 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
private _closeTemplate: TemplateRef<any>;
private _sticky = false;
private _positionSettings: PositionSettings = TooltipPositionSettings;
private _showTriggers = new Set(['pointerenter']);
private _hideTriggers = new Set(['pointerleave', 'click']);

constructor(
private _element: ElementRef,
@Optional() private _navigationService: IgxNavigationService,
private _viewContainerRef: ViewContainerRef,
private _renderer: Renderer2,
private _envInjector: EnvironmentInjector
) {
super(_element, _navigationService);
}
private _abortController = new AbortController();

/**
* @hidden
*/
@HostListener('click')
public override onClick() {
if (!this.target.collapsed) {
this._hideOnInteraction();
} else if (this.target.timeoutId) {
if (
this.target.timeoutId &&
this.target.collapsed &&
!this._showTriggers.has('click')
) {
clearTimeout(this.target.timeoutId);
this.target.timeoutId = null;
}
Expand All @@ -358,46 +403,22 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
/**
* @hidden
*/
@HostListener('mouseenter')
public onMouseEnter() {
public onShow(): void {
this._checksBeforeShowing(() => this._showOnInteraction());
}

/**
* @hidden
*/
@HostListener('mouseleave')
public onMouseLeave() {
if (this.tooltipDisabled) {
public onHide() : void {
if (this.tooltipDisabled || this.target.collapsed) {
return;
}

this._checkOutletAndOutsideClick();
this._hideOnInteraction();
}

/**
* @hidden
*/
public onTouchStart() {
this._checksBeforeShowing(() => this._showOnInteraction());
}

/**
* @hidden
*/
public onDocumentTouchStart(event) {
if (this.tooltipDisabled || this?.target?.tooltipTarget !== this) {
return;
}

if (this.nativeElement !== event.target &&
!this.nativeElement.contains(event.target)
) {
this._hideOnInteraction();
}
}

/**
* @hidden
*/
Expand All @@ -421,7 +442,8 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
}
});

this.nativeElement.addEventListener('touchstart', this.onTouchStart = this.onTouchStart.bind(this), { passive: true });
this.removeEventListeners();
this.addEventListeners();
}

/**
Expand All @@ -438,7 +460,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
*/
public ngOnDestroy() {
this.hideTooltip();
this.nativeElement.removeEventListener('touchstart', this.onTouchStart);
this.removeEventListeners();
this._destroyCloseButton();
this._destroy$.next();
this._destroy$.complete();
Expand Down Expand Up @@ -470,6 +492,24 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
return Object.assign({}, this._overlayDefaults, this.overlaySettings);
}

private addEventListeners(): void {
const options = { passive: true, signal: this._abortController.signal };

this.onShow = this.onShow.bind(this);
for (const each of this._showTriggers) {
this.nativeElement.addEventListener(each, this.onShow, options);
}
this.onHide = this.onHide.bind(this);
for (const each of this._hideTriggers) {
this.nativeElement.addEventListener(each, this.onHide, options);
}
}

private removeEventListeners(): void {
this._abortController.abort();
this._abortController = new AbortController();
}

private _checkOutletAndOutsideClick(): void {
if (this.outlet) {
this._overlayDefaults.outlet = this.outlet;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { useAnimation } from '@angular/animations';
import { fadeOut, scaleInCenter } from 'igniteui-angular/animations';

export const TooltipRegexes = Object.freeze({
/** Used for parsing the strings passed in the tooltip `show/hide-trigger` properties. */
triggers: /[,\s]+/,

/** Matches horizontal `Placement` end positions. `left-end` | `right-end` */
horizontalEnd: /^(left|right)-end$/,

Expand Down Expand Up @@ -330,3 +333,9 @@ export const PositionsMap = new Map<Placement, PositionSettings>([
verticalStartPoint: VerticalAlignment.Bottom,
}]
]);

export function parseTriggers(triggers: string): Set<string> {
return new Set(
(triggers ?? '').split(TooltipRegexes.triggers).filter((s) => s.trim())
);
}
Loading
Loading