forked from Gitlink/forgeplus-react
94 lines
2.5 KiB
JavaScript
94 lines
2.5 KiB
JavaScript
import React, { useEffect, useRef, useMemo } from 'react'
|
|
import 'katex/dist/katex.min.css'
|
|
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
|
|
import 'code-prettify'
|
|
import dompurify from 'dompurify';
|
|
|
|
import { renderToString } from 'katex'
|
|
|
|
const preRegex = /<pre[^>]*>/g
|
|
function _unescape(str) {
|
|
let div = document.createElement('div')
|
|
div.innerHTML = str
|
|
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
|
|
}
|
|
|
|
|
|
|
|
export default ({
|
|
value = '',
|
|
className,
|
|
style = {},
|
|
url
|
|
}) => {
|
|
let str = String(value);
|
|
const html = useMemo(() => {
|
|
let rs = marked(str);
|
|
const math_expressions = getMathExpressions();
|
|
if (str.match(/\[TOC\]/)) {
|
|
rs = rs.replace("<p>[TOC]</p>", getTocContent())
|
|
cleanToc()
|
|
}
|
|
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
|
|
const { type, expression } = math_expressions[capture];
|
|
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
|
|
})
|
|
rs = rs.replace(/▁/g, "▁▁▁")
|
|
resetMathExpressions()
|
|
return dompurify.sanitize(rs)
|
|
}, [str]);
|
|
|
|
// 锚点跳转,链接地址里含#对应的id
|
|
useEffect(()=>{
|
|
if(url && url.hash && html){
|
|
let u = url.hash;
|
|
if(u){
|
|
let id = decodeURIComponent(u.split("#")[1]);
|
|
let ele = document.getElementById(id);
|
|
if(ele){
|
|
window.scrollTo(0, ele.offsetTop + 120);
|
|
}
|
|
}
|
|
}
|
|
},[url,html])
|
|
|
|
const el = useRef();
|
|
function onAncherHandler(e) {
|
|
let target = e.target;
|
|
if (target.tagName.toUpperCase() === 'A') {
|
|
let ancher = target.getAttribute('href');
|
|
if (ancher && ancher.startsWith('#')) {
|
|
e.preventDefault()
|
|
let viewEl = document.getElementById(ancher.replace('#', ''))
|
|
if (viewEl) {
|
|
viewEl.scrollIntoView(true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (el.current && html) {
|
|
if (html.match(preRegex)) {
|
|
window.PR.prettyPrint()
|
|
}
|
|
}
|
|
if (el.current) {
|
|
el.current.addEventListener('click', onAncherHandler)
|
|
return () => {
|
|
el.current.removeEventListener('click', onAncherHandler)
|
|
resetMathExpressions()
|
|
cleanToc()
|
|
}
|
|
}
|
|
}, [html, el.current, onAncherHandler])
|
|
return (
|
|
<div
|
|
ref={el}
|
|
style={style}
|
|
className={`${className ? className : ''} markdown-body`}
|
|
dangerouslySetInnerHTML={{ __html: html }}
|
|
></div>
|
|
)
|
|
}
|