58 lines
2.1 KiB
TypeScript
58 lines
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
|
|
}
|