I have BBCode parsing issue inside spoiler tag. So, for example, when I add [hr] or any other BBCode
tag, it breaks the spoiler:
BBCode:
[spoiler=Title]#ff7f50 hex color red value is 255, green value is 127 and the blue value of its RGB is 80.
[hr]
Cylindrical-coordinate representations (also known as HSL) of color #ff7f50 hue: 0.04 , saturation: 1.00 and the lightness value of ff7f50 is 0.66.[/spoiler]
Screenshot:
As you can see on the image, the text after [hr]
tag displays outside the spoiler.
Code:
InfoSpoiler.tsx:
`import React, {useState} from "react";
import CustomTooltip from "./CustomTooltip";
const InfoSpoiler = ({isDefaultSpoilerTitle, spoilerContentVisibility, spoilerTitle, spoilerContent}) => {
const [isSpoilerVisible, setSpoilerVisible] = useState(spoilerContentVisibility);
const handleSpoilerState = (event) => {
setSpoilerVisible(!isSpoilerVisible);
};
return (
<div className="spoiler">
<CustomTooltip msg="Click to open/close">
<p className={`${isDefaultSpoilerTitle ? "spoiler-title" : "spoiler-title-centered"}`} onClick={handleSpoilerState}><i className={`notification-icon fa-solid ${isSpoilerVisible ? "fa-minus" : "fa-plus"}`}></i>{spoilerTitle}</p>
</CustomTooltip>
{isSpoilerVisible && (
typeof spoilerContent === "string" ? (
<div className="spoiler-content" dangerouslySetInnerHTML={{__html: spoilerContent}} />
) : (
<div className="spoiler-content">{spoilerContent}</div>
)
)}
</div>
);
};
export default InfoSpoiler;`
CustomTooltip.tsx:
`import React from "react";
import "react-tippy/dist/tippy.css";
import {Tooltip} from "react-tippy";
export type Position =
| "top"
| "top-start"
| "top-end"
| "bottom"
| "bottom-start"
| "bottom-end"
| "left"
| "left-start"
| "left-end"
| "right"
| "right-start"
| "right-end";
export type Size = "small" | "regular" | "big";
export type Theme = "dark" | "light" | "transparent";
interface IToolTip {
children: React.ReactNode;
msg: string;
pos?: Position,
tooltipSize?: Size,
tooltipTheme?: Theme
}
const CustomTooltip = ({children, msg, pos = "top", tooltipSize = "big", tooltipTheme = "light"} : IToolTip) => {
return (
<Tooltip title={msg} position={pos} size={tooltipSize} theme={tooltipTheme} followCursor={true}>{children}</Tooltip>
);
};
export default CustomTooltip;`
utilsGeneral.ts:
`const validateHtml = (data) => {
return data
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};`
BBCodeComponent.tsx:
`import React, {useEffect} from "react";
import ReactDOM from "react-dom";
import CustomTooltip from "./CustomTooltip";
import {validateHtml} from "../../../utils/utilsGeneral";
import InfoSpoiler from "./InfoSpoiler";
const BBCodeComponent = ({content}) => {
useEffect(() => {
const handleTooltip = () => {
const tooltipWrappers = document.querySelectorAll(".tooltip-wrapper");
tooltipWrappers.forEach((wrapper) => {
const title = wrapper.getAttribute("data-title");
const src = wrapper.getAttribute("data-src");
if (title && src) {
ReactDOM.render(<CustomTooltip msg={title}><img src={src} alt={title} /></CustomTooltip>, wrapper);
}
});
};
const handleSpoiler = () => {
const spoilerWrappers = document.querySelectorAll(".spoiler-wrapper");
spoilerWrappers.forEach((wrapper) => {
const title = wrapper.getAttribute("data-title");
const content = wrapper.getAttribute("data-content");
if (title && content) {
ReactDOM.render(<InfoSpoiler isDefaultSpoilerTitle={true} spoilerContentVisibility={false} spoilerTitle={title} spoilerContent={parseBBCode(content)} />, wrapper);
}
});
};
handleTooltip();
handleSpoiler();
}, [content]);
const parseBBCode = (text) => {
return text
.replace(/\[b\](.*?)\[\/b\]/g, "<strong>$1</strong>")
.replace(/\[i\](.*?)\[\/i\]/g, "<em>$1</em>")
.replace(/\[u\](.*?)\[\/u\]/g, "<u>$1</u>")
.replace(/\[s\](.*?)\[\/s\]/g, "<s>$1</s>")
.replace(/\[inline\](.*?)\[\/inline\]/g, "<div class=\"inline-tag\">$1</div>")
.replace(/\[left\](.*?)\[\/left\]/g, "<div class=\"left-tag\">$1</div>")
.replace(/\[center\](.*?)\[\/center\]/g, "<div class=\"center-tag\">$1</div>")
.replace(/\[right\](.*?)\[\/right\]/g, "<div class=\"right-tag\">$1</div>")
.replace(/\[img=(.*?)\](.*?)\[\/img\]/g, (match, title, src) => {
return `<div class=\"tooltip-wrapper\" data-title=\"${title}\" data-src=\"${src}\"><img src=\"${src}\" alt=\"${title}\"></div>`;
})
.replace(/\[quote\](.*?)\[\/quote\]/g, "<blockquote class=\"quote-tag\">$1</blockquote>")
.replace(/\[url=(.*?)\](.*?)\[\/url\]/g, "<a class=\"url-tag\" href=\"$1\">$2</a>")
.replace(/\[code\]([\s\S]*?)\[\/code\]/g, (match, content) => {
return `<pre class=\"code-tag\">${validateHtml(content)}</pre>`;
})
.replace(/\[spoiler=(.*?)\]([\s\S]*?)\[\/spoiler\]/g, (match, title, content) => {
return `<div class=\"spoiler-wrapper\" data-title=\"${title}\" data-content=\"${validateHtml(content.trim())}\"></div>`;
})
.replace(/\[hr\]/g, "<div class=\"hr-wrapper\"><hr class=\"horizontal-line-tag\"></div>")
.replace(/\[li\](.*?)\[\/li\]/g, "<li class=\"list-tag\">$1</li>")
.replace(/\[color=(\#[0-9A-F]{6}|[a-z]+|[rgb(\d{1,3},\d{1,3},\d{1,3}(\s?))]+)\](.*?)\[\/color\]/g, "<span style=\"color: $1\">$2</span>")
.replace(/\[youtube\](.*?)\[\/youtube\]/g, (match, url) => {
const videoID = url.split("v=")[1];
return `<div class="youtube-container"><iframe width=\"640\" height=\"510\" src=\"https://www.youtube.com/embed/${videoID}\" loading=\"lazy\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe></div>`;
});
};
return <div dangerouslySetInnerHTML={{__html: parseBBCode(content)}} />;
};
export default BBCodeComponent;`
These BBCode tags work well when outside the spoiler but fails to render inside the spoiler.
HTML output from a browser:
`<div><div class="spoiler-wrapper" data-title="Title" data-content="#ff7f50 hex color red value is 255, green value is 127 and the blue value of its RGB is 80.
<div class=" hr-wrapper"=""><div class="spoiler"><div class="" data-tooltipped="" aria-describedby="tippy-tooltip-59" data-original-title="Click to open/close" style="display: inline;"><p class="spoiler-title"><i class="notification-icon fa-solid fa-minus"></i>Title</p></div><div class="spoiler-content">#ff7f50 hex color red value is 255, green value is 127 and the blue value of its RGB is 80.
</div></div></div>
Cylindrical-coordinate representations (also known as HSL) of color #ff7f50 hue: 0.04 , saturation: 1.00 and the lightness value of ff7f50 is 0.66."></div>`
Any ideas how to fix this issue? Thanks.
Top comments (0)