|
1 | | -import React from "react"; |
2 | | -// TODO: Add stronger typing |
3 | | -const HTMLPreview = (props: any) => { |
| 1 | +import React, { useEffect, useRef } from "react"; |
| 2 | + |
| 3 | +const STEGA_REGEX = /[\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFEFF]|\uDB40[\uDC20-\uDC7F]/g; |
| 4 | + |
| 5 | +interface HTMLPreviewProps { |
| 6 | + html?: string; |
| 7 | +} |
| 8 | + |
| 9 | +const HTMLPreview = (props: HTMLPreviewProps) => { |
4 | 10 | const { html } = props; |
5 | | - if (!html) { |
| 11 | + const containerRef = useRef<HTMLDivElement>(null); |
| 12 | + const cleanHtml = typeof html === "string" ? html.replace(STEGA_REGEX, "") : ""; |
| 13 | + |
| 14 | + useEffect(() => { |
| 15 | + const container = containerRef.current; |
| 16 | + if (!container || !cleanHtml) return; |
| 17 | + |
| 18 | + // Re-execute any inline <script> tags so interactive SVG/HTML widgets work inside Sanity Studio preview |
| 19 | + const scripts = Array.from(container.querySelectorAll("script")); |
| 20 | + for (const oldScript of scripts) { |
| 21 | + const newScript = document.createElement("script"); |
| 22 | + for (const attr of Array.from(oldScript.attributes)) { |
| 23 | + newScript.setAttribute(attr.name, attr.value); |
| 24 | + } |
| 25 | + newScript.text = oldScript.textContent || ""; |
| 26 | + oldScript.parentNode?.replaceChild(newScript, oldScript); |
| 27 | + } |
| 28 | + }, [cleanHtml]); |
| 29 | + |
| 30 | + if (!cleanHtml) { |
6 | 31 | return <div>Add some HTML</div>; |
7 | 32 | } |
8 | | - return <div dangerouslySetInnerHTML={{ __html: html }} />; |
| 33 | + return <div ref={containerRef} dangerouslySetInnerHTML={{ __html: cleanHtml }} />; |
9 | 34 | }; |
10 | 35 |
|
11 | | -const preview = (props: any) => <HTMLPreview {...props} />; |
12 | | - |
| 36 | +const preview = (props: HTMLPreviewProps) => <HTMLPreview {...props} />; |
13 | 37 | export default preview; |
0 commit comments