58 lines
No EOL
2.1 KiB
TypeScript
58 lines
No EOL
2.1 KiB
TypeScript
import { cloneElement, isValidElement, type ReactNode } from "react";
|
|
|
|
/**
|
|
* Recursively traverses a ReactNode tree and extracts the text content.
|
|
* @param node - The ReactNode to process.
|
|
* @returns A string containing the concatenated text content of the ReactNode tree.
|
|
*/
|
|
export function getTextContent(node: ReactNode): string {
|
|
if (node === null || typeof node === "boolean") return "";
|
|
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
if (Array.isArray(node)) return node.map(getTextContent).join("");
|
|
if (isValidElement(node)) {
|
|
const props = node.props as { children?: ReactNode };
|
|
return getTextContent(props.children);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Recursively traverses a ReactNode tree and removes leading whitespace and a specified pattern from the beginning of text nodes.
|
|
* @param node - The ReactNode to process.
|
|
* @param pattern - The regular expression pattern to match and remove from the beginning of text nodes.
|
|
* @returns A new ReactNode with the leading whitespace and specified pattern removed from text nodes.
|
|
*/
|
|
export function stripLeadingMatch(node: ReactNode, pattern: RegExp): ReactNode {
|
|
if (typeof node === "string") {
|
|
const trimmed = node.replace(/^\s+/, "");
|
|
if (pattern.test(trimmed)) {
|
|
return trimmed.replace(pattern, "").replace(/^\s+/, "");
|
|
}
|
|
return node;
|
|
}
|
|
if (Array.isArray(node)) {
|
|
let stripped = false;
|
|
return node.map((child) => {
|
|
if (stripped) return child;
|
|
if (typeof child === "string" && child.trim() === "") return child; // skip whitespace nodes
|
|
if (typeof child === "string") {
|
|
const trimmed = child.replace(/^\s+/, "");
|
|
if (pattern.test(trimmed)) {
|
|
stripped = true;
|
|
return trimmed.replace(pattern, "").replace(/^\s+/, "");
|
|
}
|
|
return child;
|
|
}
|
|
if (isValidElement(child)) {
|
|
stripped = true;
|
|
return stripLeadingMatch(child, pattern);
|
|
}
|
|
return child;
|
|
});
|
|
}
|
|
if (isValidElement(node)) {
|
|
const props = node.props as { children?: ReactNode };
|
|
return cloneElement(node, {}, stripLeadingMatch(props.children, pattern));
|
|
}
|
|
return node;
|
|
} |