submissions
This commit is contained in:
@@ -1,14 +1,113 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
interface LaTextContainerProps {
|
||||
content: string;
|
||||
declare global {
|
||||
interface Window {
|
||||
MathJax?: {
|
||||
startup?: { promise?: Promise<void> };
|
||||
typesetPromise?: (elements?: Element[]) => Promise<void>;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const LaTextContainer: React.FC<LaTextContainerProps> = ({ content }) => {
|
||||
interface MediaFile {
|
||||
id: number;
|
||||
fileName: string;
|
||||
mediaUrl: string;
|
||||
}
|
||||
|
||||
return <div>
|
||||
{content}
|
||||
</div>;
|
||||
interface LaTextContainerProps {
|
||||
html: string;
|
||||
latex: string;
|
||||
mediaFiles?: MediaFile[];
|
||||
}
|
||||
|
||||
let mathJaxPromise: Promise<void> | null = null;
|
||||
|
||||
const loadMathJax = () => {
|
||||
if (mathJaxPromise) return mathJaxPromise;
|
||||
|
||||
mathJaxPromise = new Promise<void>((resolve, reject) => {
|
||||
if (window.MathJax?.typesetPromise) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
(window as any).MathJax = {
|
||||
tex: {
|
||||
inlineMath: [["$$$", "$$$"]],
|
||||
displayMath: [["$$$$$$", "$$$$$$"]],
|
||||
processEscapes: true,
|
||||
},
|
||||
options: {
|
||||
skipHtmlTags: ["script", "noscript", "style", "textarea", "pre", "code"],
|
||||
},
|
||||
startup: { typeset: false },
|
||||
};
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.id = "mathjax-script";
|
||||
script.src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";
|
||||
script.async = true;
|
||||
|
||||
script.onload = () => {
|
||||
window.MathJax?.startup?.promise?.then(resolve).catch(reject);
|
||||
};
|
||||
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
||||
return mathJaxPromise;
|
||||
};
|
||||
|
||||
const replaceImages = (html: string, latex: string, mediaFiles?: MediaFile[]) => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, "text/html");
|
||||
|
||||
const latexImageNames = Array.from(latex.matchAll(/\\includegraphics\{(.+?)\}/g)).map(
|
||||
(match) => match[1]
|
||||
);
|
||||
|
||||
const imgs = doc.querySelectorAll<HTMLImageElement>("img.tex-graphics");
|
||||
|
||||
imgs.forEach((img, idx) => {
|
||||
const imageName = latexImageNames[idx];
|
||||
if (!imageName || !mediaFiles) return;
|
||||
const mediaFile = mediaFiles.find((f) => f.fileName === imageName);
|
||||
if (mediaFile) img.src = mediaFile.mediaUrl;
|
||||
});
|
||||
|
||||
return doc.body.innerHTML;
|
||||
};
|
||||
|
||||
const LaTextContainer: React.FC<LaTextContainerProps> = ({ html, latex, mediaFiles }) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [processedHtml, setProcessedHtml] = useState<string>(html);
|
||||
|
||||
// 1️⃣ Обновляем HTML при изменении входных данных
|
||||
useEffect(() => {
|
||||
setProcessedHtml(replaceImages(html, latex, mediaFiles));
|
||||
}, [html, latex, mediaFiles]);
|
||||
|
||||
// 2️⃣ После рендера обновленного HTML применяем MathJax
|
||||
useEffect(() => {
|
||||
const renderMath = () => {
|
||||
if (containerRef.current && window.MathJax?.typesetPromise) {
|
||||
window.MathJax.typesetPromise([containerRef.current]).catch(console.error);
|
||||
}
|
||||
};
|
||||
|
||||
loadMathJax().then(renderMath).catch(console.error);
|
||||
}, [processedHtml]); // 👈 ключевой момент — триггерим именно по processedHtml
|
||||
|
||||
return (
|
||||
<div
|
||||
className="latex-container"
|
||||
ref={containerRef}
|
||||
dangerouslySetInnerHTML={{ __html: processedHtml }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LaTextContainer;
|
||||
|
||||
Reference in New Issue
Block a user