import React, { ComponentProps } from "react"; import { RawEditorOptions, TinyMCE, Editor } from "./tinymce"; import { twMerge } from "tailwind-merge"; import twuiSlugToNormalText from "../../utils/slug-to-normal-text"; import Border from "../../elements/Border"; import useTinyMCE from "./useTinyMCE"; export type TinyMCEEditorProps = { options?: RawEditorOptions; editorRef?: React.MutableRefObject; setEditor?: React.Dispatch>; wrapperProps?: React.DetailedHTMLProps< React.HTMLAttributes, HTMLDivElement >; wrapperWrapperProps?: React.DetailedHTMLProps< React.HTMLAttributes, HTMLDivElement >; borderProps?: ComponentProps; defaultValue?: string; name?: KeyType; changeHandler?: (content: string) => void; showLabel?: boolean; useParentCSS?: boolean; placeholder?: string; }; /** * # Tiny MCE Editor Component * @className_wrapper twui-rte-wrapper */ export default function TinyMCEEditor({ options, editorRef, setEditor: passedSetEditor, wrapperProps, defaultValue, changeHandler, wrapperWrapperProps, borderProps, name, showLabel, useParentCSS, placeholder, }: TinyMCEEditorProps) { const { tinyMCE } = useTinyMCE(); const editorComponentRef = React.useRef(null); const EDITOR_VALUE_CHANGE_TIMEOUT = 500; const FINAL_HEIGHT = options?.height || 500; const [themeReady, setThemeReady] = React.useState(false); const [ready, setReady] = React.useState(false); const [darkMode, setDarkMode] = React.useState(false); const [refresh, setRefresh] = React.useState(0); const [editor, setEditor] = React.useState(); const title = name ? twuiSlugToNormalText(name) : "Rich Text"; React.useEffect(() => { if (!tinyMCE) { return; } const htmlClassName = document.documentElement.className; if (htmlClassName.match(/dark/i)) setDarkMode(true); setTimeout(() => { setThemeReady(true); }, 200); }, [tinyMCE]); let valueTimeout: any; const id = crypto.randomUUID(); React.useEffect(() => { if (!editorComponentRef.current || !themeReady || !tinyMCE) { return; } const baseUrl = process.env.NEXT_PUBLIC_TINYMCE_BASE_URL || "https://www.datasquirel.com/tinymce-public"; tinyMCE.init({ height: FINAL_HEIGHT, menubar: false, plugins: "advlist lists link image charmap preview anchor searchreplace visualblocks code fullscreen insertdatetime media table code help wordcount", toolbar: "undo redo | blocks | bold italic underline link image | bullist numlist outdent indent | removeformat code searchreplace wordcount preview insertdatetime", content_style: "body { font-family:Helvetica,Arial,sans-serif; font-size:14px; background-color: transparent }", init_instance_callback: (editor) => { setEditor(editor as any); if (editorRef) editorRef.current = editor as any; if (defaultValue) editor.setContent(defaultValue); setReady(true); // editor.on("change", (e) => { // changeHandler?.(editor.getContent()); // }); editor.on("input", (e) => { if (changeHandler) { window.clearTimeout(valueTimeout); valueTimeout = setTimeout(() => { changeHandler(editor.getContent()); }, EDITOR_VALUE_CHANGE_TIMEOUT); } }); if (useParentCSS) { useParentStyles(editor); } }, base_url: baseUrl, body_class: "twui-tinymce", placeholder, relative_urls: true, remove_script_host: true, convert_urls: false, ...options, license_key: "gpl", target: editorComponentRef.current, content_css: darkMode ? "dark" : undefined, skin: darkMode ? "oxide-dark" : undefined, }); return function () { if (!ready) return; const instance = editorComponentRef.current ? tinyMCE?.get(editorComponentRef.current?.id) : undefined; instance?.remove(); }; }, [tinyMCE, themeReady, refresh]); return (
{ console.log(`Input Detected`); }} > {showLabel && ( )}
); } function useParentStyles(editor: Editor) { const doc = editor.getDoc(); const parentStylesheets = document.styleSheets; for (const sheet of parentStylesheets) { try { if (sheet.href) { const link = doc.createElement("link"); link.rel = "stylesheet"; link.href = sheet.href; doc.head.appendChild(link); } else { const rules = sheet.cssRules || sheet.rules; if (rules) { const style = doc.createElement("style"); for (const rule of rules) { try { style.appendChild(doc.createTextNode(rule.cssText)); } catch (e) { console.warn("Could not copy CSS rule:", rule, e); } } doc.head.appendChild(style); } } } catch (e) { console.warn("Error processing stylesheet:", sheet, e); } } }