UNPKG

@augment-vir/web

Version:

A collection of augments, helpers types, functions, and classes only for web (frontend) JavaScript environments.

68 lines (67 loc) 2.63 kB
import { waitUntil } from '@augment-vir/assert'; /** * Calculate the dimensions needed for an element's text. This is a relatively expensive operation, * so it should not be called excessively. * * @category Web : Elements * @category Package : @augment-vir/web * @package [`@augment-vir/web`](https://www.npmjs.com/package/@augment-vir/web) */ export async function calculateTextDimensions(parentElement, text, customOptions) { if (!text) { return { width: 0, height: 0, }; } const textWrapperElement = document.createElement('span'); textWrapperElement.style.visibility = 'hidden'; textWrapperElement.style.position = 'absolute'; textWrapperElement.style.zIndex = '-9999'; textWrapperElement.style.opacity = '0'; textWrapperElement.style.pointerEvents = 'none'; textWrapperElement.style.userSelect = 'none'; // eslint-disable-next-line sonarjs/deprecation, @typescript-eslint/no-deprecated textWrapperElement.style.webkitUserSelect = 'none'; textWrapperElement.style.animation = 'none'; textWrapperElement.style.transition = 'none'; textWrapperElement.setAttribute('aria-hidden', 'true'); /** Automatically account for shadow roots. */ const childAttachPoint = parentElement.shadowRoot || parentElement; childAttachPoint.append(textWrapperElement); let latestSize; /** * Use resize observer instead of clientWidth, getComputedStyle, or getBoundingClientRect to * prevent a forced re-layout. See https://gist.github.com/paulirish/5d52fb081b3570c81e3a. */ const resizeObserver = new ResizeObserver((entries) => { latestSize = entries[0]?.contentRect; }); resizeObserver.observe(textWrapperElement); textWrapperElement.innerText = text; try { await waitUntil.isTruthy(() => !!latestSize, { interval: { milliseconds: 0 }, timeout: customOptions?.timeout || { seconds: 10 }, }, customOptions?.errorMessage || `Failed to calculate text size in '${parentElement.tagName}'.`); /** * No way to intentionally trigger this edge case, we're just catching it here for type * purposes */ /* node:coverage ignore next 3 */ if (!latestSize) { throw new Error('Failed to calculate text size.'); } return { height: latestSize.height, width: latestSize.width, }; } finally { if (!customOptions?.debug) { textWrapperElement.remove(); } resizeObserver.disconnect(); } }