humainflow-web/src/app/shared/truncated-tooltip/truncated-tooltip.ts

55 lines
1.8 KiB
TypeScript

// SPDX-FileCopyrightText: 2025-2026 Lucio Lelii <lucio.lelii@isti.cnr.it> - ISTI-CNR
// SPDX-License-Identifier: AGPL-3.0-or-later
// Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM.
import { AfterViewInit, Directive, ElementRef, Input, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core';
import { MatTooltip } from '@angular/material/tooltip';
/**
* Shows a title preview only when its host has actually been ellipsized. Measuring after render
* and observing size changes avoids the stale first-row-only result a template expression gives
* while a list is still laying itself out.
*/
@Directive({
selector: '[appTruncatedTooltip]',
standalone: true,
hostDirectives: [MatTooltip]
})
export class TruncatedTooltipDirective implements AfterViewInit, OnChanges, OnDestroy {
@Input({ required: true }) appTruncatedTooltip = '';
private readonly element = inject(ElementRef<HTMLElement>);
private readonly tooltip = inject(MatTooltip);
private resizeObserver: ResizeObserver | null = null;
ngAfterViewInit() {
this.tooltip.position = 'above';
this.tooltip.tooltipClass = 'title-preview-tooltip';
this.scheduleRefresh();
if (typeof ResizeObserver !== 'undefined') {
this.resizeObserver = new ResizeObserver(() => this.refresh());
this.resizeObserver.observe(this.element.nativeElement);
}
}
ngOnChanges(_changes: SimpleChanges) {
this.tooltip.message = this.appTruncatedTooltip;
this.scheduleRefresh();
}
ngOnDestroy() {
this.resizeObserver?.disconnect();
}
private scheduleRefresh() {
queueMicrotask(() => this.refresh());
}
private refresh() {
const host = this.element.nativeElement;
this.tooltip.message = this.appTruncatedTooltip;
this.tooltip.disabled = host.scrollWidth <= host.clientWidth;
}
}