Newer
Older
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { isEscapeKey, isNotNull } from '@alfa-client/tech-shared';
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 AfterViewInit, OnDestroy {
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);
this.createTooltip();
}
ngOnDestroy(): void {
this.destroy();
}
@HostListener('mouseenter')
const nativeElement: HTMLElement = this.elementRef.nativeElement;
const attachedToFocused: boolean = nativeElement.contains(document.activeElement);
this.setTooltipProperties(attachedToFocused);
}
@HostListener('mouseleave')
@HostListener('window:scroll')
hideTooltip(): void {
this.hide();
@HostListener('keydown', ['$event'])
onKeydown(e: KeyboardEvent): void {
if (isEscapeKey(e)) {
this.hide();
}
}
createTooltip(): void {
if (isNotNull(this.componentRef)) {
return;
const nativeElement: HTMLElement = this.elementRef.nativeElement;
this.componentRef = this.viewContainerRef.createComponent(TooltipComponent);
this.focusableElement =
this.interactivityChecker.isFocusable(nativeElement) ? nativeElement : this.getFocusableElement(nativeElement);
this.focusableElement.appendChild(this.componentRef.location.nativeElement);
this.setAriaLabeledBy();
}
setTooltipProperties(attachedToFocused = false): void {
if (isNull(this.componentRef)) {
return;
}
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;
this.componentRef.instance.show = true;
setAriaLabeledBy(): void {
this.renderer.setAttribute(this.focusableElement, 'aria-labeledby', this.tooltipId);
removeAriaLabeledBy(): void {
this.renderer.removeAttribute(this.focusableElement, 'aria-labeledby');
}
getFocusableElement(element: HTMLElement): HTMLElement {
return element.querySelector('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');
}
hide(): void {
if (isNull(this.componentRef)) {
return;
}
this.componentRef.instance.show = false;
}
if (isNull(this.componentRef)) {
return;
}
this.componentRef.destroy();
this.componentRef = null;
this.removeAriaLabeledBy();