import { type Directive, type DirectiveBinding } from 'vue' import { type BPopoverProps } from 'bootstrap-vue-next' import { bind, type ElementWithPopper, resolveActiveStatus, resolveContent, resolveDirectiveProps, unbind, } from '@/utils/floatingUi' interface ElementWithResizeObserver extends ElementWithPopper { $__ellipsisResizeObserver?: ResizeObserver } // An element is truncated when its rendered width is narrower than the width // its content would occupy without clipping (e.g. `text-overflow: ellipsis`). const isTruncated = (el: HTMLElement): boolean => el.offsetWidth < el.scrollWidth /** * Show the tooltip only while the element is truncated; remove it otherwise. * * @param el - The element whose truncation state drives the tooltip. * @param binding - The directive binding passed to the Vue hook. * @param props - Resolved popper props (title, tooltip flag, placement, …). */ function syncTooltipWithTruncation( el: HTMLElement, binding: DirectiveBinding, props: BPopoverProps, ): void { if (isTruncated(el)) { bind(el, binding, props) } else { unbind(el) } } /** * Watch the element for size changes and keep its tooltip in sync. * * The observer is returned so the caller can store it on the element and * disconnect it when the directive unmounts. * * @param el - The element to observe. * @param binding - The directive binding passed to the Vue hook. * @param props - Resolved popper props applied when binding the tooltip. * @returns The active `ResizeObserver` observing the element. */ function observeTruncation( el: HTMLElement, binding: DirectiveBinding, props: BPopoverProps, ): ResizeObserver { const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { syncTooltipWithTruncation(entry.target as HTMLElement, binding, props) } }) resizeObserver.observe(el) return resizeObserver } /** * Directive that attaches a tooltip to an element, but only while its text is * truncated (overflowing its box). The tooltip appears when the element is * clipped and disappears once it fits, tracked live through a `ResizeObserver`. * * @example * ```html *