import React, { useEffect, useImperativeHandle } from 'react'; import ReactMarkdown from 'react-markdown'; import gfm from 'remark-gfm'; import rehypeRaw from 'rehype-raw'; import Prism from 'prismjs'; import 'prismjs/components/prism-markup'; import { loadLang } from './langs'; export type MarkdownPreviewProps = { className?: string; source?: string; style?: React.CSSProperties; warpperElement?: React.DetailedHTMLProps, HTMLDivElement>; onScroll?: (e: React.UIEvent) => void; onMouseOver?: (e: React.MouseEvent) => void; } & Omit; export type MarkdownPreviewRef = { mdp: React.RefObject; lang: string[], } & MarkdownPreviewProps; export default React.forwardRef((props, ref) => { const { className, source, style, onScroll, onMouseOver, warpperElement = {}, ...other } = props || {}; const mdp = React.createRef(); const loadedLang = React.useRef(['markup']); useEffect(() => { highlight(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [source]); async function highlight() { if (!mdp.current) return; const codes = mdp.current.getElementsByTagName('code') as unknown as HTMLElement[]; for (const val of codes) { const tag = val.parentNode as HTMLElement; if (tag && tag.tagName === 'PRE' && /^language-/.test(val.className.trim())) { const lang = val.className.trim().replace(/^language-/, ''); try { if (!loadedLang.current.includes(lang as never)) { loadedLang.current.push(lang); await loadLang(lang); } await Prism.highlightElement(val); } catch (error) { } } } } useImperativeHandle(ref, () => ({ ...props, lang: loadedLang.current, mdp }), [mdp, props]); const cls = `wmde-markdown wmde-markdown-color ${className || ''}`; return (
{source || ''}
); });