Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | 8x 8x | import React, { useCallback, useEffect, useMemo, useRef } from "react";
import { Box, Divider, Typography } from "@mui/material";
import { DrawingGraph } from "graph2d-react";
import {
normalizeGraphAnswerForImport,
parseGraphAxisBounds,
} from "@/utils/components/question-bank/question-display/graph/graphAnswerUtils";
import {
GRAPH2D_QUESTION_BANK_CANVAS_SIZE,
drawingGraphFrameSx,
} from "@/utils/components/question-bank/question-display/graph/graphConstants";
import { useSubmissionGraphSlot } from "./SubmissionGraphContext";
/**
* Read-only Graph2D canvas for submission review. graphId + instance come from
* SubmissionGraphProvider so Graph and Graph-Multiple-Select cards on the same
* page do not share `embed-0`.
*/
const SubmissionGraphAnswer = ({ content }) => {
const { graphInstance, graphId } = useSubmissionGraphSlot();
const readyRef = useRef(false);
const rafRef = useRef([]);
const encoded = normalizeGraphAnswerForImport(content);
const bounds = useMemo(() => parseGraphAxisBounds(content), [content]);
const cancelRafs = useCallback(() => {
rafRef.current.forEach((id) => cancelAnimationFrame(id));
rafRef.current = [];
}, []);
const applyCanvas = useCallback(() => {
if (!readyRef.current || !encoded || !graphInstance || graphId === undefined) return false;
try {
graphInstance.importAnswer(encoded, graphId);
return true;
} catch (err) {
console.warn(`Failed to import graph review canvas for graphId ${graphId}:`, err);
return false;
}
}, [encoded, graphId, graphInstance]);
const importWhenReady = useCallback(() => {
cancelRafs();
applyCanvas();
const outer = requestAnimationFrame(() => {
applyCanvas();
const inner = requestAnimationFrame(() => {
applyCanvas();
});
rafRef.current.push(inner);
});
rafRef.current.push(outer);
}, [applyCanvas, cancelRafs]);
const handleGraphReady = useCallback(() => {
readyRef.current = true;
importWhenReady();
}, [importWhenReady]);
useEffect(() => {
if (!readyRef.current) return undefined;
importWhenReady();
return cancelRafs;
}, [bounds.xMax, bounds.xMin, bounds.yMax, bounds.yMin, cancelRafs, encoded, importWhenReady]);
useEffect(() => () => cancelRafs(), [cancelRafs]);
if (!encoded) {
return (
<Typography variant="body2" color="text.secondary" fontStyle="italic" sx={{ py: 1 }}>
No graph answer submitted.
</Typography>
);
}
const size = GRAPH2D_QUESTION_BANK_CANVAS_SIZE;
return (
<Box sx={drawingGraphFrameSx}>
<DrawingGraph
key={`answer-drawing-${graphId}-${bounds.xMin}-${bounds.xMax}-${bounds.yMin}-${bounds.yMax}`}
graphId={graphId}
width={size}
height={size}
instance={graphInstance}
steps={1}
readOnly
xMin={bounds.xMin}
xMax={bounds.xMax}
yMin={bounds.yMin}
yMax={bounds.yMax}
onGraphReady={handleGraphReady}
/>
</Box>
);
};
export const SubmissionGraphReview = ({ studentContent, correctContent }) => (
<Box sx={{ mt: 1.5, display: "flex", flexDirection: "column", gap: 1.5 }}>
<Box>
<Typography
variant="caption"
color="text.secondary"
sx={{ fontWeight: 600, display: "block", mb: 0.5 }}
>
Student's Answer
</Typography>
<SubmissionGraphAnswer content={studentContent} />
</Box>
{correctContent != null && correctContent !== "" && (
<>
<Divider />
<Box>
<Typography
variant="caption"
color="text.secondary"
sx={{ fontWeight: 600, display: "block", mb: 0.5 }}
>
Correct Answer
</Typography>
<SubmissionGraphAnswer content={correctContent} />
</Box>
</>
)}
</Box>
);
export default SubmissionGraphAnswer;
|