Newer
Older
import { isEscapeKey, isNotNull } from '@alfa-client/tech-shared';
import {
ComponentRef,
Directive,
ElementRef,
HostListener,
inject,
Input,
OnDestroy,
import { TooltipComponent } from './tooltip.component';
const OUTLINE_INDENT = 4; // Outline offset (2) + outline width (2)
})
export class TooltipDirective implements OnDestroy {
@Input() tooltip: string = '';
componentRef: ComponentRef<TooltipComponent> = null;
focusableElement: HTMLElement = null;
tooltipId: string;
public viewContainerRef: ViewContainerRef = inject(ViewContainerRef);
public elementRef: ElementRef<HTMLElement> = inject(ElementRef);
public renderer: Renderer2 = inject(Renderer2);
public interactivityChecker: InteractivityChecker = inject(InteractivityChecker);
ngOnDestroy(): void {
this.destroy();
}
@HostListener('mouseenter')
if (isNotNull(this.componentRef)) {
return;
}
const nativeElement: HTMLElement = this.elementRef.nativeElement;
const attachedToFocused: boolean = nativeElement.contains(document.activeElement);
this.componentRef = this.viewContainerRef.createComponent(TooltipComponent);
nativeElement.appendChild(this.componentRef.location.nativeElement);
this.setAriaDescribedBy();
this.setTooltipProperties(attachedToFocused);
}
@HostListener('mouseleave')
@HostListener('window:scroll')
@HostListener('keydown', ['$event'])
onKeydown(e: KeyboardEvent): void {
if (isEscapeKey(e)) {
this.destroy();
}
}
setTooltipProperties(attachedToFocused = false): void {
const { left, right, bottom } = this.elementRef.nativeElement.getBoundingClientRect();
this.componentRef.instance.left = (right + left) / 2;
this.componentRef.instance.top = attachedToFocused ? bottom + OUTLINE_INDENT : bottom;
this.componentRef.instance.text = this.tooltip;
this.componentRef.instance.id = this.tooltipId;
const nativeElement: HTMLElement = this.elementRef.nativeElement;
this.tooltipId = uniqueId('tooltip');
this.focusableElement =
this.interactivityChecker.isFocusable(nativeElement) ? nativeElement : this.getFocusableElement(nativeElement);
this.renderer.setAttribute(this.focusableElement, 'aria-describedby', this.tooltipId);
}
this.renderer.removeAttribute(this.focusableElement, 'aria-describedby');
}
getFocusableElement(element: HTMLElement): HTMLElement {
return element.querySelector('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');
}
if (isNull(this.componentRef)) {
return;
}
this.componentRef.destroy();
this.componentRef = null;
this.removeAriaDescribedBy();
this.focusableElement = null;