Added an exercise to write in blank spaces

This commit is contained in:
Tiago Ribeiro
2023-03-28 16:10:43 +01:00
parent 2b38f9df9b
commit 780d208675
8 changed files with 156 additions and 27 deletions

View File

@@ -1,12 +1,12 @@
import {errorButtonStyle, infoButtonStyle} from "@/constants/buttonStyles";
import {FillBlanksExercise} from "@/interfaces/exam";
import {Dialog, Transition} from "@headlessui/react";
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
import {mdiArrowLeft, mdiArrowRight} from "@mdi/js";
import Icon from "@mdi/react";
import clsx from "clsx";
import {Fragment, useState} from "react";
import reactStringReplace from "react-string-replace";
import { CommonProps } from ".";
import {CommonProps} from ".";
interface WordsPopoutProps {
words: {word: string; isDisabled: boolean}[];
@@ -79,7 +79,7 @@ export default function FillBlanks({allowRepetition, prompt, solutions, text, wo
const renderLines = (line: string) => {
return (
<span>
{reactStringReplace(line, /({{\d}})/g, (match) => {
{reactStringReplace(line, /({{\d+}})/g, (match) => {
const id = match.replaceAll(/[\{\}]/g, "");
const userSolution = userSolutions.find((x) => x.id === id);
@@ -95,28 +95,28 @@ export default function FillBlanks({allowRepetition, prompt, solutions, text, wo
return (
<>
<div className="flex flex-col">
<WordsPopout
words={words.map((word) => ({word, isDisabled: allowRepetition ? false : userSolutions.map((x) => x.solution).includes(word)}))}
isOpen={!!currentBlankId}
onCancel={() => setCurrentBlankId(undefined)}
onAnswer={(solution: string) => {
setUserSolutions((prev) => [...prev.filter((x) => x.id !== currentBlankId), {id: currentBlankId!, solution}]);
setCurrentBlankId(undefined);
}}
/>
<span className="text-lg font-medium text-center px-48">{prompt}</span>
<span>
{text.split("\n").map((line) => (
<>
{renderLines(line)}
<br />
</>
))}
</span>
</div>
<div className="flex flex-col">
<WordsPopout
words={words.map((word) => ({word, isDisabled: allowRepetition ? false : userSolutions.map((x) => x.solution).includes(word)}))}
isOpen={!!currentBlankId}
onCancel={() => setCurrentBlankId(undefined)}
onAnswer={(solution: string) => {
setUserSolutions((prev) => [...prev.filter((x) => x.id !== currentBlankId), {id: currentBlankId!, solution}]);
setCurrentBlankId(undefined);
}}
/>
<span className="text-lg font-medium text-center px-48">{prompt}</span>
<span>
{text.split("\n").map((line) => (
<>
{renderLines(line)}
<br />
</>
))}
</span>
</div>
<div className="self-end flex gap-8">
<div className="self-end flex gap-8">
<button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)} onClick={onBack}>
<div className="absolute left-4">
<Icon path={mdiArrowLeft} color="white" size={1} />

View File

@@ -61,7 +61,7 @@ export default function MultipleChoice({prompt, questions, onNext, onBack}: Mult
};
const next = () => {
if (questionIndex === questions.length) {
if (questionIndex === questions.length - 1) {
onNext();
} else {
setQuestionIndex((prev) => prev + 1);

View File

@@ -0,0 +1,56 @@
import {errorButtonStyle, infoButtonStyle} from "@/constants/buttonStyles";
import {WriteBlanksExercise} from "@/interfaces/exam";
import {mdiArrowLeft, mdiArrowRight} from "@mdi/js";
import Icon from "@mdi/react";
import clsx from "clsx";
import {useState} from "react";
import reactStringReplace from "react-string-replace";
import {CommonProps} from ".";
export default function WriteBlanks({prompt, maxWords, solutions, text, onNext, onBack}: WriteBlanksExercise & CommonProps) {
const [userSolutions, setUserSolutions] = useState<{id: string; solution: string}[]>([]);
const renderLines = (line: string) => {
return (
<span>
{reactStringReplace(line, /({{\d+}})/g, (match) => {
const id = match.replaceAll(/[\{\}]/g, "");
const userSolution = userSolutions.find((x) => x.id === id);
return <input className="input border rounded-xl px-2 text-blue-400 border-blue-400 py-1" placeholder={id} />;
})}
</span>
);
};
return (
<>
<div className="flex flex-col">
<span className="text-lg font-medium text-center px-48">{prompt}</span>
<span>
{text.split("\n").map((line) => (
<>
{renderLines(line)}
<br />
</>
))}
</span>
</div>
<div className="self-end flex gap-8">
<button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)} onClick={onBack}>
<div className="absolute left-4">
<Icon path={mdiArrowLeft} color="white" size={1} />
</div>
Back
</button>
<button className={clsx("btn btn-wide gap-4 relative text-white", infoButtonStyle)} onClick={onNext}>
Next
<div className="absolute right-4">
<Icon path={mdiArrowRight} color="white" size={1} />
</div>
</button>
</div>
</>
);
}

View File

@@ -1,7 +1,8 @@
import {Exercise, FillBlanksExercise, MatchSentencesExercise, MultipleChoiceExercise} from "@/interfaces/exam";
import {Exercise, FillBlanksExercise, MatchSentencesExercise, MultipleChoiceExercise, WriteBlanksExercise} from "@/interfaces/exam";
import dynamic from "next/dynamic";
import FillBlanks from "./FillBlanks";
import MultipleChoice from "./MultipleChoice";
import WriteBlanks from "./WriteBlanks";
const MatchSentences = dynamic(() => import("@/components/Exercises/MatchSentences"), {ssr: false});
@@ -18,5 +19,7 @@ export const renderExercise = (exercise: Exercise, onNext: () => void, onBack: (
return <MatchSentences {...(exercise as MatchSentencesExercise)} onNext={onNext} onBack={onBack} />;
case "multipleChoice":
return <MultipleChoice {...(exercise as MultipleChoiceExercise)} onNext={onNext} onBack={onBack} />;
case "writeBlanks":
return <WriteBlanks {...(exercise as WriteBlanksExercise)} onNext={onNext} onBack={onBack} />;
}
};