import { LevelPart } from "@/interfaces/exam"; import { useEffect, useRef } from "react"; interface Props { part: LevelPart, contextWord: string | undefined, setContextWordLine: React.Dispatch> } const TextComponent: React.FC = ({part, contextWord, setContextWordLine}) => { const textRef = useRef(null); const calculateLineNumbers = () => { if (textRef.current) { const computedStyle = window.getComputedStyle(textRef.current); const containerWidth = textRef.current.clientWidth; const offscreenElement = document.createElement('div'); offscreenElement.style.position = 'absolute'; offscreenElement.style.top = '-9999px'; offscreenElement.style.left = '-9999px'; offscreenElement.style.width = `${containerWidth}px`; offscreenElement.style.font = computedStyle.font; offscreenElement.style.lineHeight = computedStyle.lineHeight; offscreenElement.style.whiteSpace = 'pre-wrap'; offscreenElement.style.wordWrap = 'break-word'; offscreenElement.style.textAlign = computedStyle.textAlign as CanvasTextAlign; const paragraphs = part.context!.split('\n\n'); let currentLine = 1; let contextWordLine: number | null = null; const paragraphLineStarts: number[] = []; paragraphs.forEach((paragraph, pIndex) => { const p = document.createElement('p'); p.style.margin = '0'; p.style.padding = '0'; paragraph.split(/(\s+)/).forEach((word: string) => { const span = document.createElement('span'); span.textContent = word; p.appendChild(span); }); offscreenElement.appendChild(p); if (pIndex < paragraphs.length - 1) { const gap = document.createElement('div'); gap.style.height = '16px'; // gap-4 offscreenElement.appendChild(gap); } }); document.body.appendChild(offscreenElement); let currentLineTop: number | undefined; const elements = offscreenElement.querySelectorAll('p, div'); elements.forEach((element) => { if (element.tagName === 'P') { const spans = element.querySelectorAll('span'); paragraphLineStarts.push(currentLine); spans.forEach(span => { const rect = span.getBoundingClientRect(); const top = rect.top; if (currentLineTop === undefined || top > currentLineTop) { if (currentLineTop !== undefined) { currentLine++; } currentLineTop = top; } if (contextWord && contextWordLine === null && span.textContent?.includes(contextWord)) { contextWordLine = currentLine; } }); } else if (element.tagName === 'DIV') { // Gap currentLine++; currentLineTop = undefined; } }); if (contextWordLine) { setContextWordLine(contextWordLine); } document.body.removeChild(offscreenElement); } }; useEffect(() => { calculateLineNumbers(); const resizeObserver = new ResizeObserver(() => { calculateLineNumbers(); }); if (textRef.current) { resizeObserver.observe(textRef.current); } return () => { if (textRef.current) { resizeObserver.unobserve(textRef.current); } }; }, [part.context, contextWord]); /*if (typeof part.showContextLines === "undefined") { return (
{!!part.context && part.context .split(/\n|(\\n)/g) .filter((x) => x && x.length > 0 && x !== "\\n") .map((line, index) => (

{line}

))}
); }*/ return (
{part.context!.split('\n\n').map((line, index) => { return

{index + 1}{line}

})}
); } export default TextComponent;