import { useAnimation } from '@angular/animations';
import { Directive, OnInit, OnDestroy, Output, ElementRef, Optional, ViewContainerRef, HostListener, Input, EventEmitter } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { fadeOut } from '../../animations/fade';
import { scaleInCenter } from '../../animations/scale';
import { IgxNavigationService } from '../../core/navigation';
import { IBaseEventArgs } from '../../core/utils';
import { AutoPositionStrategy, HorizontalAlignment, PositionSettings } from '../../services/public_api';
import { IgxToggleActionDirective } from '../toggle/toggle.directive';
import { IgxTooltipComponent } from './tooltip.component';
import { IgxTooltipDirective } from './tooltip.directive';
export interface ITooltipShowEventArgs extends IBaseEventArgs {
target: IgxTooltipTargetDirective;
tooltip: IgxTooltipDirective;
cancel: boolean;
}
export interface ITooltipHideEventArgs extends IBaseEventArgs {
target: IgxTooltipTargetDirective;
tooltip: IgxTooltipDirective;
cancel: boolean;
}
/**
* **Ignite UI for Angular Tooltip Target** -
* [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tooltip)
*
* The Ignite UI for Angular Tooltip Target directive is used to mark an HTML element in the markup as one that has a tooltip.
* The tooltip target is used in combination with the Ignite UI for Angular Tooltip by assigning the exported tooltip reference to the
* target's selector property.
*
* Example:
* ```html
*
* Hello there, I am a tooltip!
* ```
*/
@Directive({
exportAs: 'tooltipTarget',
selector: '[igxTooltipTarget]',
standalone: true
})
export class IgxTooltipTargetDirective extends IgxToggleActionDirective implements OnInit, OnDestroy {
/**
* Gets/sets the amount of milliseconds that should pass before showing the tooltip.
*
* ```typescript
* // get
* let tooltipShowDelay = this.tooltipTarget.showDelay;
* ```
*
* ```html
*
*
* Hello there, I am a tooltip!
* ```
*/
@Input('showDelay')
public showDelay = 500;
/**
* Gets/sets the amount of milliseconds that should pass before hiding the tooltip.
*
* ```typescript
* // get
* let tooltipHideDelay = this.tooltipTarget.hideDelay;
* ```
*
* ```html
*
*
* Hello there, I am a tooltip!
* ```
*/
@Input('hideDelay')
public hideDelay = 500;
/**
* Specifies if the tooltip should not show when hovering its target with the mouse. (defaults to false)
* While setting this property to 'true' will disable the user interactions that shows/hides the tooltip,
* the developer will still be able to show/hide the tooltip through the API.
*
* ```typescript
* // get
* let tooltipDisabledValue = this.tooltipTarget.tooltipDisabled;
* ```
*
* ```html
*
*
* Hello there, I am a tooltip!
* ```
*/
@Input('tooltipDisabled')
public tooltipDisabled = false;
/**
* @hidden
*/
@Input('igxTooltipTarget')
public override set target(target: any) {
if (target !== null && target !== '') {
this._target = target;
}
}
/**
* @hidden
*/
public override get target(): any {
if (typeof this._target === 'string') {
return this._navigationService.get(this._target);
}
return this._target;
}
/**
* @hidden
*/
@Input()
public set tooltip(content: any) {
if (!this.target && (typeof content === 'string' || content instanceof String)) {
const tooltipComponent = this._viewContainerRef.createComponent(IgxTooltipComponent);
tooltipComponent.instance.content = content as string;
this._target = tooltipComponent.instance.tooltip;
}
}
/**
* Gets the respective native element of the directive.
*
* ```typescript
* let tooltipTargetElement = this.tooltipTarget.nativeElement;
* ```
*/
public get nativeElement() {
return this._element.nativeElement;
}
/**
* Indicates if the tooltip that is is associated with this target is currently hidden.
*
* ```typescript
* let tooltipHiddenValue = this.tooltipTarget.tooltipHidden;
* ```
*/
public get tooltipHidden(): boolean {
return !this.target || this.target.collapsed;
}
/**
* Emits an event when the tooltip that is associated with this target starts showing.
* This event is fired before the start of the countdown to showing the tooltip.
*
* ```typescript
* tooltipShowing(args: ITooltipShowEventArgs) {
* alert("Tooltip started showing!");
* }
* ```
*
* ```html
*
* Hello there, I am a tooltip!
* ```
*/
@Output()
public tooltipShow = new EventEmitter();
/**
* Emits an event when the tooltip that is associated with this target starts hiding.
* This event is fired before the start of the countdown to hiding the tooltip.
*
* ```typescript
* tooltipHiding(args: ITooltipHideEventArgs) {
* alert("Tooltip started hiding!");
* }
* ```
*
* ```html
*
* Hello there, I am a tooltip!
* ```
*/
@Output()
public tooltipHide = new EventEmitter();
private destroy$ = new Subject();
constructor(private _element: ElementRef,
@Optional() private _navigationService: IgxNavigationService, private _viewContainerRef: ViewContainerRef) {
super(_element, _navigationService);
}
/**
* @hidden
*/
@HostListener('click')
public override onClick() {
if (!this.target.collapsed) {
this.target.forceClose(this.mergedOverlaySettings);
}
}
/**
* @hidden
*/
@HostListener('mouseenter')
public onMouseEnter() {
if (this.tooltipDisabled) {
return;
}
this.checkOutletAndOutsideClick();
const shouldReturn = this.preMouseEnterCheck();
if (shouldReturn) {
return;
}
const showingArgs = { target: this, tooltip: this.target, cancel: false };
this.tooltipShow.emit(showingArgs);
if (showingArgs.cancel) {
return;
}
this.target.toBeShown = true;
this.target.timeoutId = setTimeout(() => {
this.target.open(this.mergedOverlaySettings); // Call open() of IgxTooltipDirective
this.target.toBeShown = false;
}, this.showDelay);
}
/**
* @hidden
*/
@HostListener('mouseleave')
public onMouseLeave() {
if (this.tooltipDisabled) {
return;
}
this.checkOutletAndOutsideClick();
const shouldReturn = this.preMouseLeaveCheck();
if (shouldReturn || this.target.collapsed) {
return;
}
this.target.toBeHidden = true;
this.target.timeoutId = setTimeout(() => {
this.target.close(); // Call close() of IgxTooltipDirective
this.target.toBeHidden = false;
}, this.hideDelay);
}
/**
* @hidden
*/
@HostListener('touchstart')
public onTouchStart() {
if (this.tooltipDisabled) {
return;
}
this.showTooltip();
}
/**
* @hidden
*/
@HostListener('document:touchstart', ['$event'])
public onDocumentTouchStart(event) {
if (this.tooltipDisabled) {
return;
}
if (this.nativeElement !== event.target &&
!this.nativeElement.contains(event.target)
) {
this.hideTooltip();
}
}
/**
* @hidden
*/
public override ngOnInit() {
super.ngOnInit();
const positionSettings: PositionSettings = {
horizontalDirection: HorizontalAlignment.Center,
horizontalStartPoint: HorizontalAlignment.Center,
openAnimation: useAnimation(scaleInCenter, { params: { duration: '150ms' } }),
closeAnimation: useAnimation(fadeOut, { params: { duration: '75ms' } })
};
this._overlayDefaults.positionStrategy = new AutoPositionStrategy(positionSettings);
this._overlayDefaults.closeOnOutsideClick = false;
this._overlayDefaults.closeOnEscape = true;
this.target.closing.pipe(takeUntil(this.destroy$)).subscribe((event) => {
const hidingArgs = { target: this, tooltip: this.target, cancel: false };
this.tooltipHide.emit(hidingArgs);
if (hidingArgs.cancel) {
event.cancel = true;
}
});
}
/**
* @hidden
*/
public ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
/**
* Shows the tooltip by respecting the 'showDelay' property.
*
* ```typescript
* this.tooltipTarget.showTooltip();
* ```
*/
public showTooltip() {
clearTimeout(this.target.timeoutId);
if (!this.target.collapsed) {
// if close animation has started finish it, or close the tooltip with no animation
this.target.forceClose(this.mergedOverlaySettings);
this.target.toBeHidden = false;
}
const showingArgs = { target: this, tooltip: this.target, cancel: false };
this.tooltipShow.emit(showingArgs);
if (showingArgs.cancel) {
return;
}
this.target.toBeShown = true;
this.target.timeoutId = setTimeout(() => {
this.target.open(this.mergedOverlaySettings); // Call open() of IgxTooltipDirective
this.target.toBeShown = false;
}, this.showDelay);
}
/**
* Hides the tooltip by respecting the 'hideDelay' property.
*
* ```typescript
* this.tooltipTarget.hideTooltip();
* ```
*/
public hideTooltip() {
if (this.target.collapsed && this.target.toBeShown) {
clearTimeout(this.target.timeoutId);
}
if (this.target.collapsed || this.target.toBeHidden) {
return;
}
this.target.toBeHidden = true;
this.target.timeoutId = setTimeout(() => {
this.target.close(); // Call close() of IgxTooltipDirective
this.target.toBeHidden = false;
}, this.hideDelay);
}
private checkOutletAndOutsideClick() {
if (this.outlet) {
this._overlayDefaults.outlet = this.outlet;
}
}
private get mergedOverlaySettings() {
return Object.assign({}, this._overlayDefaults, this.overlaySettings);
}
// Return true if the execution in onMouseEnter should be terminated after this method
private preMouseEnterCheck() {
// If tooltip is about to be opened
if (this.target.toBeShown) {
clearTimeout(this.target.timeoutId);
this.target.toBeShown = false;
}
// If Tooltip is opened or about to be hidden
if (!this.target.collapsed || this.target.toBeHidden) {
clearTimeout(this.target.timeoutId);
// if close animation has started finish it, or close the tooltip with no animation
this.target.forceClose(this.mergedOverlaySettings);
this.target.toBeHidden = false;
}
return false;
}
// Return true if the execution in onMouseLeave should be terminated after this method
private preMouseLeaveCheck(): boolean {
clearTimeout(this.target.timeoutId);
// If tooltip is about to be opened
if (this.target.toBeShown) {
this.target.toBeShown = false;
this.target.toBeHidden = false;
return true;
}
return false;
}
}