components_jsx-from-text.js

import { React } from '@gravityforms/libraries';

/**
 * @module JSXFromText
 * @description A component to create JSX from text by replacing custom tags and wrapping inner text in passed components.
 *
 * @since 3.1.0
 *
 * @param {object} props               Component props.
 * @param {string} props.closingSymbol The closing symbol for the custom tags.
 * @param {string} props.openingSymbol The opening symbol for the custom tags.
 * @param {string} props.text          The text to process.
 * @param {Array}  props.tokens        The tokens to replace in the text.
 *
 * @return {JSX.Element} The JSXFromText component.
 */
export default function JSXFromText( {
	closingSymbol = '%%',
	openingSymbol = '%%',
	text = '',
	tokens = [],
} ) {
	const tokenMap = tokens.reduce( ( acc, token ) => {
		acc[ token.key ] = token;
		return acc;
	}, {} );

	const processText = ( t ) => {
		const regex = new RegExp( `${ openingSymbol }(.*?)${ closingSymbol }([\\s\\S]*?)${ openingSymbol }\\1${ closingSymbol }`, 'g' );
		let lastIndex = 0;
		const elements = [];

		t.replace( regex, ( match, tokenKey, innerText, index ) => {
			// Push the text before the matched tag
			elements.push( t.slice( lastIndex, index ) );

			// Create and push the JSX element
			const token = tokenMap[ tokenKey ];
			if ( token ) {
				const Component = token.component;
				elements.push( <Component { ...token.props } key={ `${ tokenKey }-${ index }` }>{ innerText }</Component> );
			} else {
				elements.push( innerText );
			}

			lastIndex = index + match.length;
		} );

		// Push any remaining text after the last match
		elements.push( t.slice( lastIndex ) );

		return elements;
	};

	return <>{ processText( text ) }</>;
}