/**
* Internal dependencies
*/
import { trimHtml } from './trim-html';
/**
* External dependencies
*/
type Markers = {
end: number;
middle: number;
start: number;
};
/**
* Truncate some HTML content to a given length.
*
* @param {string} html HTML that will be truncated.
* @param {number} length Length to truncate the string to.
* @param {string} ellipsis Character to append to truncated content.
*/
export const truncateHtml = (
html: string,
length: number,
ellipsis = '...'
): string => {
const trimmed = trimHtml( html, {
suffix: ellipsis,
limit: length,
} );
return trimmed.html;
};
/**
* Move string markers. Used by calculateLength.
*
* @param {Markers} markers Markers for clamped content.
* @param {number} currentHeight Current height of clamped content.
* @param {number} maxHeight Max height of the clamped content.
*/
const moveMarkers = (
markers: Markers,
currentHeight: number,
maxHeight: number
): Markers => {
if ( currentHeight <= maxHeight ) {
markers.start = markers.middle + 1;
} else {
markers.end = markers.middle - 1;
}
return markers;
};
/**
* Calculate how long the content can be based on the maximum number of lines allowed, and client height.
*
* @param {string} originalContent Content to be clamped.
* @param {HTMLElement} targetElement Element which will contain the clamped content.
* @param {number} maxHeight Max height of the clamped content.
*/
const calculateLength = (
originalContent: string,
targetElement: HTMLElement,
maxHeight: number
): number => {
let markers: Markers = {
start: 0,
middle: 0,
end: originalContent.length,
};
while ( markers.start <= markers.end ) {
markers.middle = Math.floor( ( markers.start + markers.end ) / 2 );
// We set the innerHTML directly in the DOM here so we can reliably check the clientHeight later in moveMarkers.
targetElement.innerHTML = truncateHtml(
originalContent,
markers.middle
);
markers = moveMarkers( markers, targetElement.clientHeight, maxHeight );
}
return markers.middle;
};
/**
* Clamp lines calculates the height of a line of text and then limits it to the
* value of the lines prop. Content is updated once limited.
*
* @param {string} originalContent Content to be clamped.
* @param {HTMLElement} targetElement Element which will contain the clamped content.
* @param {number} maxHeight Max height of the clamped content.
* @param {string} ellipsis Character to append to clamped content.
* @return {string} clamped content
*/
export const clampLines = (
originalContent: string,
targetElement: HTMLElement,
maxHeight: number,
ellipsis: string
): string => {
const length = calculateLength( originalContent, targetElement, maxHeight );
return truncateHtml( originalContent, length - ellipsis.length, ellipsis );
};