Files
encoach_frontend/src/components/Exercises/FillBlanks.tsx
2023-03-23 16:22:48 +00:00

114 lines
3.7 KiB
TypeScript

import {errorButtonStyle, infoButtonStyle} from "@/constants/buttonStyles";
import {FillBlanksExercise} from "@/interfaces/exam";
import {Dialog, Transition} from "@headlessui/react";
import clsx from "clsx";
import {Fragment, useState} from "react";
import reactStringReplace from "react-string-replace";
interface WordsPopoutProps {
words: {word: string; isDisabled: boolean}[];
isOpen: boolean;
onCancel: () => void;
onAnswer: (answer: string) => void;
}
function WordsPopout({words, isOpen, onCancel, onAnswer}: WordsPopoutProps) {
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={onCancel}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95">
<Dialog.Panel className="w-fit transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all flex flex-col">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
List of words
</Dialog.Title>
<div className="mt-4 grid grid-cols-3 gap-4">
{words.map((word) => (
<button
key={word.word}
onClick={() => onAnswer(word.word)}
disabled={word.isDisabled}
className={clsx("btn btn-wide gap-4 relative text-white", infoButtonStyle)}>
{word.word}
</button>
))}
</div>
<div className="mt-4 self-end">
<button onClick={onCancel} className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)}>
Close
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
);
}
export default function FillBlanks({allowRepetition, prompt, solutions, text, words}: FillBlanksExercise) {
const [userSolutions, setUserSolutions] = useState<{id: string; solution: string}[]>([]);
const [currentBlankId, setCurrentBlankId] = useState<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 (
<button className="border-2 rounded-xl px-4 text-blue-400 border-blue-400" onClick={() => setCurrentBlankId(id)}>
{userSolution ? userSolution.solution : id}
</button>
);
})}
</span>
);
};
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>
);
}