@augment-vir/web
Version:
A collection of augments, helpers types, functions, and classes only for web (frontend) JavaScript environments.
72 lines (71 loc) • 2.67 kB
JavaScript
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 @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();
}
}