import React, { ReactChild } from 'react'; import escapeRegExp from 'lodash/escapeRegExp'; /** * * Wraps every instance of `wrap` found in `str` in the provided `element`. * * E.g. `wrapJSX('Apple', 'ap', 'em') => Apple` * * @param str * @param wrap * @param element * @returns `JSX.Element` * @internal */ export const wrapJSX = ( str: string, wrap?: string, element?: keyof HTMLElementTagNameMap, ): JSX.Element => { if (wrap && element) { const cleanWrap = escapeRegExp(wrap); const regex = new RegExp(cleanWrap, 'gi'); const matches = str.matchAll(regex); if (matches) { const outArray = str.split('') as Array; /** * For every match, splice it into the "string", * wrapped in the React element */ // Consider adding --downlevelIteration TS flag so we don't need Array.from for (const match of Array.from(matches)) { const matchIndex = match.index ?? -1; const matchContent = match[0]; const matchLength = matchContent.length; const key = matchIndex + matchContent + matchLength; // We create a replacement array that's // the same length as the match we're deleting, // in order to keep the matchIndexes aligned // with the indexes of the output array const replacement = new Array(matchLength).fill(''); replacement[0] = React.createElement(element, { key }, matchContent); outArray.splice(matchIndex, matchLength, ...replacement); } return <>{outArray}; } return <>{str}; } return <>{str}; };