forgeplus-react/src/components/render-html.jsx

94 lines
2.5 KiB
React
Raw Normal View History

2020-12-11 18:14:25 +08:00
import React, { useEffect, useRef, useMemo } from 'react'
import 'katex/dist/katex.min.css'
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
2020-08-12 18:13:18 +08:00
import 'code-prettify'
2020-12-15 18:01:16 +08:00
import dompurify from 'dompurify';
2020-05-09 09:43:44 +08:00
2020-12-11 18:14:25 +08:00
import { renderToString } from 'katex'
2020-04-28 17:23:34 +08:00
2020-12-11 18:14:25 +08:00
const preRegex = /<pre[^>]*>/g
2020-08-12 18:13:18 +08:00
function _unescape(str) {
let div = document.createElement('div')
div.innerHTML = str
2020-12-11 18:14:25 +08:00
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
2020-08-12 18:13:18 +08:00
}
2020-05-09 09:43:44 +08:00
2020-12-11 18:14:25 +08:00
2020-12-15 18:01:16 +08:00
2020-12-11 18:14:25 +08:00
export default ({
value = '',
className,
style = {},
2020-12-15 18:01:16 +08:00
url
2020-12-11 18:14:25 +08:00
}) => {
2020-12-15 18:01:16 +08:00
let str = String(value);
2020-08-12 18:13:18 +08:00
const html = useMemo(() => {
let rs = marked(str);
2020-12-11 18:14:25 +08:00
const math_expressions = getMathExpressions();
2020-08-12 18:13:18 +08:00
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]
2020-12-11 18:14:25 +08:00
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
2020-08-12 18:13:18 +08:00
})
rs = rs.replace(/▁/g, "▁▁▁")
resetMathExpressions()
2020-12-11 18:14:25 +08:00
return dompurify.sanitize(rs)
2020-12-15 18:01:16 +08:00
}, [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){
2020-12-16 11:37:56 +08:00
window.scrollTo(0, ele.offsetTop + 220);
2020-12-15 18:01:16 +08:00
}
}
}
},[url])
2020-04-28 17:23:34 +08:00
2020-12-11 18:14:25 +08:00
const el = useRef();
2020-05-09 09:43:44 +08:00
function onAncherHandler(e) {
2020-08-12 18:13:18 +08:00
let target = e.target
if (target.tagName.toUpperCase() === 'A') {
let ancher = target.getAttribute('href')
if (ancher.startsWith('#')) {
e.preventDefault()
let viewEl = document.getElementById(ancher.replace('#', ''))
2020-05-09 09:43:44 +08:00
if (viewEl) {
2020-12-11 18:14:25 +08:00
viewEl.scrollIntoView(true)
2020-05-09 09:43:44 +08:00
}
}
}
}
2020-04-28 17:23:34 +08:00
useEffect(() => {
if (el.current && html) {
2020-04-29 15:27:55 +08:00
if (html.match(preRegex)) {
2020-08-12 18:13:18 +08:00
window.PR.prettyPrint()
2020-04-28 17:23:34 +08:00
}
}
2020-05-09 09:43:44 +08:00
if (el.current) {
2020-08-12 18:13:18 +08:00
el.current.addEventListener('click', onAncherHandler)
2020-05-09 09:43:44 +08:00
return () => {
2020-08-12 18:13:18 +08:00
el.current.removeEventListener('click', onAncherHandler)
resetMathExpressions()
cleanToc()
}
2020-05-09 09:43:44 +08:00
}
2020-08-12 18:13:18 +08:00
}, [html, el.current, onAncherHandler])
2020-12-11 18:14:25 +08:00
return (
<div
ref={el}
style={style}
className={`${className ? className : ''} markdown-body`}
dangerouslySetInnerHTML={{ __html: html }}
></div>
)
2020-08-12 18:13:18 +08:00
}