괄호 안의 항을 2, 3, 4 항으로 설정하는 기능 추가
This commit is contained in:
@@ -23,10 +23,11 @@ export default function FeedbackPanel({
|
||||
onNext,
|
||||
}: FeedbackPanelProps) {
|
||||
const nextButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const signedTerms = [
|
||||
problem.a,
|
||||
problem.operator === "-" ? -problem.b : problem.b,
|
||||
] as const;
|
||||
const signedTerms = problem.terms.map((term, index) => {
|
||||
if (index === 0) return term;
|
||||
|
||||
return problem.operators[index - 1] === "-" ? -term : term;
|
||||
});
|
||||
const feedbackItems = signedTerms.map((term, index) => ({
|
||||
formulaLatex: `${formatSignedNumber(
|
||||
problem.multiplier,
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { FocusEvent, FormEvent } from "react";
|
||||
import { DESKTOP_INPUT_DEVICE_QUERY } from "@/lib/input-device";
|
||||
import KatexRenderer from "@/components/math/KatexRenderer";
|
||||
|
||||
export type ActiveAnswerInput = 0 | 1;
|
||||
export type ActiveAnswerInput = number;
|
||||
|
||||
const ANSWER_INPUT_PATTERN = /^-?\d*(?:\.\d*)?$/;
|
||||
const KEYPAD_KEYS = [
|
||||
@@ -36,13 +36,11 @@ export interface NumberAnswerFormHandle {
|
||||
}
|
||||
|
||||
interface NumberAnswerFormProps {
|
||||
answerA: string;
|
||||
answerB: string;
|
||||
answers: string[];
|
||||
activeInput: ActiveAnswerInput;
|
||||
error: string;
|
||||
pendingAnswerLabels: [string, string];
|
||||
onAnswerAChange: (value: string) => void;
|
||||
onAnswerBChange: (value: string) => void;
|
||||
pendingAnswerLabels: string[];
|
||||
onAnswerChange: (index: ActiveAnswerInput, value: string) => void;
|
||||
onActiveInputChange: (input: ActiveAnswerInput) => void;
|
||||
onInputBlur?: () => void;
|
||||
onInputFocus?: () => void;
|
||||
@@ -54,13 +52,11 @@ const NumberAnswerForm = forwardRef<
|
||||
NumberAnswerFormProps
|
||||
>(function NumberAnswerForm(
|
||||
{
|
||||
answerA,
|
||||
answerB,
|
||||
answers,
|
||||
activeInput,
|
||||
error,
|
||||
pendingAnswerLabels,
|
||||
onAnswerAChange,
|
||||
onAnswerBChange,
|
||||
onAnswerChange,
|
||||
onActiveInputChange,
|
||||
onInputBlur,
|
||||
onInputFocus,
|
||||
@@ -75,17 +71,16 @@ const NumberAnswerForm = forwardRef<
|
||||
const [keypadOpen, setKeypadOpen] = useState(false);
|
||||
const [desktopInputDevice, setDesktopInputDevice] = useState(false);
|
||||
|
||||
const activeAnswer = activeInput === 0 ? answerA : answerB;
|
||||
const activeLabel = activeInput === 0 ? "첫째 항" : "둘째 항";
|
||||
const activeAnswer = answers[activeInput] ?? "";
|
||||
const activeLabel = `${activeInput + 1}항`;
|
||||
const activeDescription = `${activeLabel}의 답을 입력하세요.`;
|
||||
const activeEnterKeyHint = activeInput === 0 ? "next" : "done";
|
||||
const activeAnswerChange =
|
||||
activeInput === 0 ? onAnswerAChange : onAnswerBChange;
|
||||
const lastInputIndex = answers.length - 1;
|
||||
const activeEnterKeyHint = activeInput < lastInputIndex ? "next" : "done";
|
||||
|
||||
function updateAnswer(value: string) {
|
||||
if (!ANSWER_INPUT_PATTERN.test(value)) return;
|
||||
|
||||
activeAnswerChange(value);
|
||||
onAnswerChange(activeInput, value);
|
||||
}
|
||||
|
||||
function handleInputFocus() {
|
||||
@@ -154,16 +149,9 @@ const NumberAnswerForm = forwardRef<
|
||||
};
|
||||
}, []);
|
||||
|
||||
function setAnswer(input: ActiveAnswerInput, value: string) {
|
||||
if (input === 0) {
|
||||
onAnswerAChange(value);
|
||||
} else {
|
||||
onAnswerBChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMinus() {
|
||||
activeAnswerChange(
|
||||
onAnswerChange(
|
||||
activeInput,
|
||||
activeAnswer.startsWith("-") ? activeAnswer.slice(1) : `-${activeAnswer}`,
|
||||
);
|
||||
focusInput();
|
||||
@@ -182,14 +170,14 @@ const NumberAnswerForm = forwardRef<
|
||||
function moveToInput(input: ActiveAnswerInput, reset = false) {
|
||||
onActiveInputChange(input);
|
||||
if (reset) {
|
||||
setAnswer(input, "");
|
||||
onAnswerChange(input, "");
|
||||
}
|
||||
focusInput();
|
||||
}
|
||||
|
||||
function handleEnter() {
|
||||
if (activeInput === 0) {
|
||||
moveToInput(1);
|
||||
if (activeInput < lastInputIndex) {
|
||||
moveToInput(activeInput + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,18 +185,11 @@ const NumberAnswerForm = forwardRef<
|
||||
focusInput();
|
||||
}
|
||||
|
||||
const answerButtons = [
|
||||
{
|
||||
index: 0 as const,
|
||||
label: "첫째 항",
|
||||
value: answerA,
|
||||
},
|
||||
{
|
||||
index: 1 as const,
|
||||
label: "둘째 항",
|
||||
value: answerB,
|
||||
},
|
||||
];
|
||||
const answerButtons = answers.map((value, index) => ({
|
||||
index,
|
||||
label: `${index + 1}항`,
|
||||
value,
|
||||
}));
|
||||
|
||||
return (
|
||||
<form
|
||||
@@ -334,9 +315,9 @@ const NumberAnswerForm = forwardRef<
|
||||
onPointerDown={(event) => event.preventDefault()}
|
||||
onClick={handleEnter}
|
||||
aria-label={
|
||||
activeInput === 0
|
||||
? "첫째 항 입력 완료 후 둘째 항으로 이동"
|
||||
: "둘째 항 입력 완료 후 제출"
|
||||
activeInput < lastInputIndex
|
||||
? "현재 항 입력 완료 후 다음 항으로 이동"
|
||||
: "마지막 항 입력 완료 후 제출"
|
||||
}
|
||||
className="col-span-2 inline-flex h-12 items-center justify-center rounded-md bg-emerald-700 text-base font-semibold text-white shadow-sm transition hover:bg-emerald-800 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
|
||||
>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Link from "next/link";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export type PreparationProblemCount = 5 | 10;
|
||||
export type PreparationMaxTermCount = 2 | 3 | 4;
|
||||
|
||||
interface ParenthesesPreparationClientProps {
|
||||
eyebrow: string;
|
||||
@@ -12,11 +13,33 @@ interface ParenthesesPreparationClientProps {
|
||||
notice: string;
|
||||
playHref: string;
|
||||
selectedProblemCount: PreparationProblemCount;
|
||||
selectedMaxTermCount: PreparationMaxTermCount;
|
||||
onProblemCountChange: (count: PreparationProblemCount) => void;
|
||||
onMaxTermCountChange: (count: PreparationMaxTermCount) => void;
|
||||
onStart: () => void;
|
||||
}
|
||||
|
||||
const problemCounts: PreparationProblemCount[] = [5, 10];
|
||||
const maxTermCounts: PreparationMaxTermCount[] = [2, 3, 4];
|
||||
|
||||
const buttonGroupClassName =
|
||||
"inline-flex gap-1 rounded-md border border-slate-300 bg-white p-1 shadow-sm";
|
||||
|
||||
function optionButtonClassName({
|
||||
selected,
|
||||
disabled = false,
|
||||
}: {
|
||||
selected: boolean;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return `h-10 min-w-16 rounded px-4 text-base font-semibold transition focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 ${
|
||||
selected ? "bg-emerald-700 text-white" : "text-slate-700 hover:bg-slate-100"
|
||||
} ${
|
||||
disabled
|
||||
? "cursor-not-allowed bg-slate-100 text-slate-400 hover:bg-slate-100"
|
||||
: ""
|
||||
}`;
|
||||
}
|
||||
|
||||
const bracketKinds = [
|
||||
{
|
||||
@@ -46,7 +69,9 @@ export default function ParenthesesPreparationClient({
|
||||
notice,
|
||||
playHref,
|
||||
selectedProblemCount,
|
||||
selectedMaxTermCount,
|
||||
onProblemCountChange,
|
||||
onMaxTermCountChange,
|
||||
onStart,
|
||||
}: ParenthesesPreparationClientProps) {
|
||||
const startLinkRef = useRef<HTMLAnchorElement>(null);
|
||||
@@ -74,7 +99,7 @@ export default function ParenthesesPreparationClient({
|
||||
문제 수
|
||||
</legend>
|
||||
<div
|
||||
className="inline-flex gap-1 rounded-md border border-slate-300 bg-white p-1 shadow-sm"
|
||||
className={buttonGroupClassName}
|
||||
role="group"
|
||||
aria-label="문제 수 선택"
|
||||
>
|
||||
@@ -87,11 +112,7 @@ export default function ParenthesesPreparationClient({
|
||||
type="button"
|
||||
onClick={() => onProblemCountChange(count)}
|
||||
aria-pressed={selected}
|
||||
className={`h-10 min-w-16 rounded px-4 text-base font-semibold transition focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 ${
|
||||
selected
|
||||
? "bg-emerald-700 text-white"
|
||||
: "text-slate-700 hover:bg-slate-100"
|
||||
}`}
|
||||
className={optionButtonClassName({ selected })}
|
||||
>
|
||||
{count}
|
||||
</button>
|
||||
@@ -100,39 +121,63 @@ export default function ParenthesesPreparationClient({
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="space-y-3">
|
||||
<legend className="text-sm font-semibold text-slate-700">
|
||||
괄호 종류
|
||||
</legend>
|
||||
<div
|
||||
className="inline-flex gap-1 rounded-md border border-slate-300 bg-white p-1 shadow-sm"
|
||||
role="group"
|
||||
aria-label="괄호 종류 선택"
|
||||
>
|
||||
{bracketKinds.map((bracketKind) => (
|
||||
<button
|
||||
key={bracketKind.label}
|
||||
type="button"
|
||||
disabled={bracketKind.disabled}
|
||||
aria-pressed={bracketKind.selected}
|
||||
aria-label={`${bracketKind.name} ${
|
||||
bracketKind.disabled ? "준비 중" : "선택됨"
|
||||
}`}
|
||||
className={`h-10 min-w-16 rounded px-4 text-base font-semibold transition focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2 ${
|
||||
bracketKind.selected
|
||||
? "bg-emerald-700 text-white"
|
||||
: "text-slate-700 hover:bg-slate-100"
|
||||
} ${
|
||||
bracketKind.disabled
|
||||
? "cursor-not-allowed bg-slate-100 text-slate-400 hover:bg-slate-100"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{bracketKind.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</fieldset>
|
||||
<div className="flex flex-col gap-6 sm:flex-row sm:items-start">
|
||||
<fieldset className="space-y-3">
|
||||
<legend className="text-sm font-semibold text-slate-700">
|
||||
괄호 종류
|
||||
</legend>
|
||||
<div
|
||||
className={buttonGroupClassName}
|
||||
role="group"
|
||||
aria-label="괄호 종류 선택"
|
||||
>
|
||||
{bracketKinds.map((bracketKind) => (
|
||||
<button
|
||||
key={bracketKind.label}
|
||||
type="button"
|
||||
disabled={bracketKind.disabled}
|
||||
aria-pressed={bracketKind.selected}
|
||||
aria-label={`${bracketKind.name} ${
|
||||
bracketKind.disabled ? "준비 중" : "선택됨"
|
||||
}`}
|
||||
className={optionButtonClassName({
|
||||
selected: bracketKind.selected,
|
||||
disabled: bracketKind.disabled,
|
||||
})}
|
||||
>
|
||||
{bracketKind.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="space-y-3">
|
||||
<legend className="text-sm font-semibold text-slate-700">
|
||||
괄호 안 최대 항 수
|
||||
</legend>
|
||||
<div
|
||||
className={buttonGroupClassName}
|
||||
role="group"
|
||||
aria-label="괄호 안 최대 항 수 선택"
|
||||
>
|
||||
{maxTermCounts.map((count) => {
|
||||
const selected = selectedMaxTermCount === count;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={count}
|
||||
type="button"
|
||||
onClick={() => onMaxTermCountChange(count)}
|
||||
aria-pressed={selected}
|
||||
className={optionButtonClassName({ selected })}
|
||||
>
|
||||
{count}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Link
|
||||
|
||||
@@ -15,50 +15,54 @@ interface ScoreSummaryProps {
|
||||
onRestart: () => void;
|
||||
}
|
||||
|
||||
function formatAnswerPairLatex(first: number, second: number) {
|
||||
const operator = second < 0 ? "-" : "+";
|
||||
function formatAnswerLatex(terms: number[]) {
|
||||
return terms
|
||||
.map((term, index) => {
|
||||
if (index === 0) return `${term}`;
|
||||
|
||||
return `${first} ${operator} ${Math.abs(second)}`;
|
||||
const operator = term < 0 ? "-" : "+";
|
||||
return `${operator} ${Math.abs(term)}`;
|
||||
})
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function renderUserAnswerPair(result: GradeResult) {
|
||||
const secondOperator = result.userAnswer[1] < 0 ? "-" : "+";
|
||||
const firstCorrect = result.userAnswer[0] === result.correctAnswer[0];
|
||||
const secondCorrect = result.userAnswer[1] === result.correctAnswer[1];
|
||||
|
||||
return (
|
||||
<span className="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
<span className={firstCorrect ? undefined : "text-red-700"}>
|
||||
<KatexRenderer latex={`${result.userAnswer[0]}`} output="mathml" />
|
||||
</span>
|
||||
<span className={secondCorrect ? undefined : "text-red-700"}>
|
||||
<KatexRenderer
|
||||
latex={`${secondOperator} ${Math.abs(result.userAnswer[1])}`}
|
||||
output="mathml"
|
||||
/>
|
||||
</span>
|
||||
{result.userAnswer.map((term, index) => {
|
||||
const operator = term < 0 ? "-" : "+";
|
||||
const correct = term === result.correctAnswer[index];
|
||||
const displayLatex =
|
||||
index === 0 ? `${term}` : `${operator} ${Math.abs(term)}`;
|
||||
|
||||
return (
|
||||
<span key={index} className={correct ? undefined : "text-red-700"}>
|
||||
<KatexRenderer latex={displayLatex} output="mathml" />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function renderCorrectAnswerPair(result: GradeResult) {
|
||||
const secondOperator = result.correctAnswer[1] < 0 ? "-" : "+";
|
||||
const firstCorrect = result.userAnswer[0] === result.correctAnswer[0];
|
||||
const secondCorrect = result.userAnswer[1] === result.correctAnswer[1];
|
||||
|
||||
return (
|
||||
<span className="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
<span className={firstCorrect ? undefined : "font-bold text-blue-700"}>
|
||||
<KatexRenderer latex={`${result.correctAnswer[0]}`} output="mathml" />
|
||||
</span>
|
||||
<span
|
||||
className={secondCorrect ? undefined : "font-bold text-blue-700"}
|
||||
>
|
||||
<KatexRenderer
|
||||
latex={`${secondOperator} ${Math.abs(result.correctAnswer[1])}`}
|
||||
output="mathml"
|
||||
/>
|
||||
</span>
|
||||
{result.correctAnswer.map((term, index) => {
|
||||
const operator = term < 0 ? "-" : "+";
|
||||
const correct = result.userAnswer[index] === term;
|
||||
const displayLatex =
|
||||
index === 0 ? `${term}` : `${operator} ${Math.abs(term)}`;
|
||||
|
||||
return (
|
||||
<span
|
||||
key={index}
|
||||
className={correct ? undefined : "font-bold text-blue-700"}
|
||||
>
|
||||
<KatexRenderer latex={displayLatex} output="mathml" />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -102,10 +106,7 @@ export default function ScoreSummary({
|
||||
|
||||
<ol className="space-y-3">
|
||||
{solvedItems.map(({ problem, result }, index) => {
|
||||
const userAnswerLatex = formatAnswerPairLatex(
|
||||
result.userAnswer[0],
|
||||
result.userAnswer[1],
|
||||
);
|
||||
const userAnswerLatex = formatAnswerLatex(result.userAnswer);
|
||||
|
||||
return (
|
||||
<AnswerResultLine
|
||||
|
||||
@@ -6,11 +6,11 @@ import KatexRenderer from "@/components/math/KatexRenderer";
|
||||
import type { VariableTermChoice } from "@/lib/engine/variable-types";
|
||||
|
||||
interface VariableChoiceAnswerFormProps {
|
||||
choices: [VariableTermChoice[], VariableTermChoice[]];
|
||||
selectedChoiceIds: [string | null, string | null];
|
||||
activeTermIndex: 0 | 1;
|
||||
pendingChoiceLabels: [string, string];
|
||||
onSelectChoice: (termIndex: 0 | 1, choiceId: string) => void;
|
||||
choices: VariableTermChoice[][];
|
||||
selectedChoiceIds: (string | null)[];
|
||||
activeTermIndex: number;
|
||||
pendingChoiceLabels: string[];
|
||||
onSelectChoice: (termIndex: number, choiceId: string) => void;
|
||||
}
|
||||
|
||||
export default function VariableChoiceAnswerForm({
|
||||
@@ -29,7 +29,7 @@ export default function VariableChoiceAnswerForm({
|
||||
firstChoiceRef.current?.focus();
|
||||
}, [activeTermIndex]);
|
||||
|
||||
function getSelectedChoice(index: 0 | 1) {
|
||||
function getSelectedChoice(index: number) {
|
||||
const selectedChoiceId = selectedChoiceIds[index];
|
||||
|
||||
if (!selectedChoiceId) return null;
|
||||
@@ -42,7 +42,7 @@ export default function VariableChoiceAnswerForm({
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-500">
|
||||
{activeTermIndex === 0 ? "첫째 항" : "둘째 항"}을 고르세요.
|
||||
{activeTermIndex + 1}항을 고르세요.
|
||||
</p>
|
||||
</div>
|
||||
<ol
|
||||
@@ -51,7 +51,7 @@ export default function VariableChoiceAnswerForm({
|
||||
>
|
||||
{selectedChoiceIds.map((choiceId, index) => {
|
||||
const selected = activeTermIndex === index;
|
||||
const selectedChoice = getSelectedChoice(index as 0 | 1);
|
||||
const selectedChoice = getSelectedChoice(index);
|
||||
const completed = choiceId !== null && selectedChoice !== null;
|
||||
const statusLabel =
|
||||
completed && selectedChoice
|
||||
@@ -95,7 +95,7 @@ export default function VariableChoiceAnswerForm({
|
||||
<div
|
||||
className="mt-5 grid gap-3 sm:grid-cols-2"
|
||||
role="group"
|
||||
aria-label={`${activeTermIndex === 0 ? "첫째 항" : "둘째 항"} 보기 선택`}
|
||||
aria-label={`${activeTermIndex + 1}항 보기 선택`}
|
||||
>
|
||||
{activeChoices.map((choice, index) => {
|
||||
const selected = selectedChoiceIds[activeTermIndex] === choice.id;
|
||||
|
||||
@@ -6,7 +6,6 @@ import AnswerResultLine from "@/components/game/AnswerResultLine";
|
||||
import type {
|
||||
VariableGradeResult,
|
||||
VariableParenthesisProblem,
|
||||
VariableParenthesisTerm,
|
||||
} from "@/lib/engine/variable-types";
|
||||
import { formatVariableTermLatex } from "@/lib/engine/variable-parenthesis";
|
||||
|
||||
@@ -21,12 +20,6 @@ function formatSignedNumber(value: number) {
|
||||
return value < 0 ? `(${value})` : `${value}`;
|
||||
}
|
||||
|
||||
function formatSignedTerm(term: VariableParenthesisTerm, sign: 1 | -1) {
|
||||
const coefficient = term.coefficient * sign;
|
||||
|
||||
return formatVariableTermLatex({ ...term, coefficient });
|
||||
}
|
||||
|
||||
export default function VariableFeedbackPanel({
|
||||
result,
|
||||
problem,
|
||||
@@ -34,14 +27,18 @@ export default function VariableFeedbackPanel({
|
||||
onNext,
|
||||
}: VariableFeedbackPanelProps) {
|
||||
const nextButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const signedTerms = [
|
||||
formatSignedTerm(problem.firstTerm, 1),
|
||||
formatSignedTerm(problem.secondTerm, problem.operator === "-" ? -1 : 1),
|
||||
] as const;
|
||||
const signedTerms = problem.terms.map((term, index) =>
|
||||
formatVariableTermLatex({
|
||||
...term,
|
||||
coefficient:
|
||||
index > 0 && problem.operators[index - 1] === "-"
|
||||
? -term.coefficient
|
||||
: term.coefficient,
|
||||
}),
|
||||
);
|
||||
const feedbackItems = signedTerms.map((termLatex, index) => {
|
||||
const termIndex = index as 0 | 1;
|
||||
const userAnswer = result.userAnswer[termIndex];
|
||||
const correctAnswer = result.correctAnswer[termIndex];
|
||||
const userAnswer = result.userAnswer[index];
|
||||
const correctAnswer = result.correctAnswer[index];
|
||||
|
||||
return {
|
||||
formulaLatex: `${formatSignedNumber(problem.multiplier)}\\times ${termLatex}`,
|
||||
|
||||
@@ -21,14 +21,10 @@ interface VariableScoreSummaryProps {
|
||||
}
|
||||
|
||||
function renderTermPair(
|
||||
terms: [DistributedVariableTerm | null, DistributedVariableTerm | null],
|
||||
comparisonTerms: [DistributedVariableTerm | null, DistributedVariableTerm | null],
|
||||
terms: (DistributedVariableTerm | null)[],
|
||||
comparisonTerms: (DistributedVariableTerm | null)[],
|
||||
mode: "user" | "correct",
|
||||
) {
|
||||
const secondTerm = terms[1];
|
||||
const secondOperator =
|
||||
secondTerm && secondTerm.coefficient < 0 ? "-" : "+";
|
||||
|
||||
return (
|
||||
<span className="inline-flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
{terms.map((term, index) => {
|
||||
@@ -49,10 +45,9 @@ function renderTermPair(
|
||||
: correct
|
||||
? undefined
|
||||
: "font-bold text-blue-700";
|
||||
const operator = term.coefficient < 0 ? "-" : "+";
|
||||
const displayLatex =
|
||||
index === 1
|
||||
? `${secondOperator} ${term.latex.replace(/^-/, "")}`
|
||||
: term.latex;
|
||||
index === 0 ? term.latex : `${operator} ${term.latex.replace(/^-/, "")}`;
|
||||
|
||||
return (
|
||||
<span key={index} className={className}>
|
||||
@@ -111,8 +106,9 @@ export default function VariableScoreSummary({
|
||||
key={problem.key}
|
||||
expressionLatex={problem.latexBefore}
|
||||
userAnswerLatex={formatVariableExpressionLatex([
|
||||
result.userAnswer[0] ?? result.correctAnswer[0],
|
||||
result.userAnswer[1] ?? result.correctAnswer[1],
|
||||
...result.correctAnswer.map(
|
||||
(term, termIndex) => result.userAnswer[termIndex] ?? term,
|
||||
),
|
||||
])}
|
||||
correctAnswerLatex={problem.latexAfter}
|
||||
correct={result.correct}
|
||||
|
||||
Reference in New Issue
Block a user