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

163 lines
5.1 KiB
React
Raw Normal View History

2023-04-13 15:04:46 +08:00
import React, { useEffect, useRef, useMemo , useState } from 'react'
2024-01-02 10:14:11 +08:00
// import 'katex/dist/katex.min.css';
2020-12-11 18:14:25 +08:00
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
2023-02-13 17:24:22 +08:00
import 'code-prettify';
2020-12-15 18:01:16 +08:00
import dompurify from 'dompurify';
2022-11-24 17:37:54 +08:00
import { getEmoji } from '../forge/Main/emoji';
2023-10-30 14:04:10 +08:00
import { emojiList } from '../common/util/emoji';
2023-04-13 15:04:46 +08:00
import axios from 'axios';
2023-07-06 11:28:47 +08:00
import imgError from '../images/img-error.png'
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
2022-11-24 17:37:54 +08:00
const preRegex = /<pre[^>]*>/g;
const strRegexSub = /:([a-zA-Z_]+):/g;
2023-05-04 16:07:06 +08:00
const quoteRegex = /\[[#][0-9]{0,}\]\(\/[A-Za-z0-9]{0,}\/[A-Za-z0-9]{0,}\/issues\/[0-9]{0,}\)/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 = {},
2023-04-13 15:04:46 +08:00
url,
owner=undefined,
projectsId=undefined
2020-12-11 18:14:25 +08:00
}) => {
2023-04-13 15:04:46 +08:00
const [ issues , setIssues ] = useState([]);
useEffect(()=>{
if(owner&&projectsId){
getIssueList();
}
},[owner,projectsId])
function getIssueList(){
2023-05-15 14:25:00 +08:00
let matchQuote = str.match(quoteRegex);
let array = [];
if(matchQuote && matchQuote.length>0){
for(var x=0;x<matchQuote.length;x++){
let getIndex = matchQuote[x].match(/#(\S*)\]/)[1];
getIndex && array.push(getIndex);
2023-04-13 15:04:46 +08:00
}
2023-05-15 14:25:00 +08:00
axios.get(`/${owner}/${projectsId}/issues/index_to_name`,{params:{
index:array
}}).then(result=>{
if(result){
setIssues(result.data);
}
})
}
2023-04-13 15:04:46 +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()
}
2022-12-26 15:26:57 +08:00
// 循环匹配所有emoji
2022-11-24 17:37:54 +08:00
let matchStr = str.match(strRegexSub);
if(matchStr && matchStr.length>0){
for(var i=0;i < matchStr.length;i++){
rs = rs.replace(matchStr[i],getEmoji(matchStr[i]));
}
}
2023-10-30 14:04:10 +08:00
if(rs){
for(let i = 0;i<emojiList.length;i++){
if(rs.indexOf(emojiList[i].code)>=0){
const reg = '/\\'+emojiList[i].code+'/g'
switch(emojiList[i].index){
case 1:
rs = rs.replace(eval(reg), `<img style="width:24px;" src="https://testforum.trustie.net/editormd/emoji/${emojiList[i].name}.png"/>`)
case 2:
rs = rs.replace(eval(reg), `<img style="width:24px;" src="https://twemoji.maxcdn.com/36x36/${emojiList[i].name}.png"/>`)
case 3:
rs = rs.replace(eval(reg), `<i class="fa fa-emoji fa-${emojiList[i].name}"></i>`)
case 4:
rs = rs.replace(eval(reg), `<i class="${emojiList[i].name}"></i>`)
}
}
}
}
2023-04-13 15:04:46 +08:00
if(owner && projectsId && issues && issues.length>0){
2023-05-15 14:25:00 +08:00
for(var x=0;x<issues.length;x++){
let item = issues[x];
let content = item.id ? `<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}:${item.subject}</a>` : `<span>#${item.project_issues_index}(已删除)</span>`;
2024-01-13 09:51:34 +08:00
rs = rs.split(`<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}</a>`).join(content);
2023-04-13 15:04:46 +08:00
}
}
2022-11-24 17:37:54 +08:00
2020-08-12 18:13:18 +08:00
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
2021-12-17 10:13:00 +08:00
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
})
2023-07-06 11:28:47 +08:00
rs = dompurify.sanitize(rs.replace(/▁/g, "▁▁▁"))
2024-01-13 09:51:34 +08:00
rs = rs.split('<img ').join(`<img onerror="javascript:this.src='${ imgError }';"`) // 替换replaceall
2020-08-12 18:13:18 +08:00
resetMathExpressions()
2023-07-06 11:28:47 +08:00
return rs
2023-04-13 15:04:46 +08:00
}, [str,issues]);
2020-12-15 18:01:16 +08:00
// 锚点跳转,链接地址里含#对应的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){
2023-01-13 10:53:14 +08:00
window.scrollTo(0, ele.offsetTop);
2020-12-15 18:01:16 +08:00
}
}
}
2021-05-25 10:05:14 +08:00
},[url,html])
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) {
2021-05-25 10:05:14 +08:00
let target = e.target;
2020-08-12 18:13:18 +08:00
if (target.tagName.toUpperCase() === 'A') {
2021-05-25 10:05:14 +08:00
let ancher = target.getAttribute('href');
if (ancher && ancher.startsWith('#')) {
2020-08-12 18:13:18 +08:00
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
}