Files
PrivyDrop/frontend/hooks/useRichTextToPlainText.ts
T
2025-06-22 21:34:54 +08:00

85 lines
2.7 KiB
TypeScript
Executable File

import { useEffect, useState } from "react";
// We convert the function into a custom Hook useRichTextToPlainText.
// This allows us to use React's lifecycle methods to detect if we are in a browser environment.
// Use useState and useEffect to detect if we are in a browser environment.
// useEffect only runs on the client side, so we can safely set isBrowser to true in it.
function useRichTextToPlainText() {
const [isBrowser, setIsBrowser] = useState(false);
useEffect(() => {
setIsBrowser(true);
}, []);
const richTextToPlainText = (richText: string): string => {
if (!isBrowser) {
return richText; // On the server side, return the original text directly
}
// Create a temporary DOM element
const tempElement = document.createElement("div");
// Set the rich text content as the innerHTML of the temporary element
tempElement.innerHTML = richText;
// Process direct text nodes (text not inside any block-level elements)
// Wrap them in a div for consistent processing
const wrapTextNodes = (element: HTMLElement) => {
const childNodes = Array.from(element.childNodes);
childNodes.forEach((node) => {
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {
const wrapper = document.createElement("div");
wrapper.textContent = node.textContent;
node.replaceWith(wrapper);
}
});
};
wrapTextNodes(tempElement);
// Process all block-level elements
const blockElements = [
"div",
"p",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"pre",
];
blockElements.forEach((tag) => {
tempElement.querySelectorAll(tag).forEach((element) => {
// If the element content is empty or only contains <br>, replace it with a double newline
if (!element.textContent?.trim() || element.innerHTML === "<br>") {
element.replaceWith("\n\n");
} else {
// Otherwise, add a newline after the content
element.replaceWith(element.textContent + "\n");
}
});
});
// Process <br> tags
tempElement.querySelectorAll("br").forEach((br) => {
br.replaceWith("\n");
});
// Get and process the plain text
let plainText = tempElement.textContent || tempElement.innerText || "";
// Process consecutive newline characters
plainText = plainText
.replace(/\n{3,}/g, "\n\n") // Replace 3 or more consecutive newline characters with 2
.replace(/^\n+/, "") // Remove leading newline characters
.replace(/\n+$/, "") // Remove trailing newline characters
.trim(); // Trim leading/trailing whitespace
return plainText;
};
return richTextToPlainText;
}
export default useRichTextToPlainText;