From 780d2086759fefdbdc8395f74bb6bf12073bc5a3 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Tue, 28 Mar 2023 16:10:43 +0100 Subject: [PATCH] Added an exercise to write in blank spaces --- package.json | 1 + src/components/Exercises/FillBlanks.tsx | 48 +++++++++--------- src/components/Exercises/MultipleChoice.tsx | 2 +- src/components/Exercises/WriteBlanks.tsx | 56 +++++++++++++++++++++ src/components/Exercises/index.tsx | 5 +- src/demo/listening.json | 27 ++++++++++ src/interfaces/exam.ts | 13 ++++- yarn.lock | 31 ++++++++++++ 8 files changed, 156 insertions(+), 27 deletions(-) create mode 100644 src/components/Exercises/WriteBlanks.tsx diff --git a/package.json b/package.json index 6c95333b..6ae3f361 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "react-chartjs-2": "^5.2.0", "react-dom": "18.2.0", "react-lineto": "^3.3.0", + "react-player": "^2.12.0", "react-string-replace": "^1.1.0", "typescript": "4.9.5", "zustand": "^4.3.6" diff --git a/src/components/Exercises/FillBlanks.tsx b/src/components/Exercises/FillBlanks.tsx index 8ae5e073..42ede5d1 100644 --- a/src/components/Exercises/FillBlanks.tsx +++ b/src/components/Exercises/FillBlanks.tsx @@ -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 ( - {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 ( <> -
- ({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); - }} - /> - {prompt} - - {text.split("\n").map((line) => ( - <> - {renderLines(line)} -
- - ))} -
-
+
+ ({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); + }} + /> + {prompt} + + {text.split("\n").map((line) => ( + <> + {renderLines(line)} +
+ + ))} +
+
-
+
+ +
+ + ); +} diff --git a/src/components/Exercises/index.tsx b/src/components/Exercises/index.tsx index 3fd98dc7..e94da47d 100644 --- a/src/components/Exercises/index.tsx +++ b/src/components/Exercises/index.tsx @@ -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 ; case "multipleChoice": return ; + case "writeBlanks": + return ; } }; diff --git a/src/demo/listening.json b/src/demo/listening.json index 46e9e03b..15cc9d51 100644 --- a/src/demo/listening.json +++ b/src/demo/listening.json @@ -59,6 +59,33 @@ ] } ] + }, + { + "type": "writeBlanks", + "prompt": "Complete the notes below by writing NO MORE THAN THREE WORDS in the spaces provided.", + "maxWords": 3, + "text": "The Government plans to give ${{14}} to assist the farmers. This money was to be spent on improving Sydney’s {{15}} but has now been re-allocated. Australia has experienced its worst drought in over fifty years. Farmers say that the money will not help them because it is {{16}}.", + "solutions": [ + { + "id": "14", + "solution": [ + "250 million" + ] + }, + { + "id": "15", + "solution": [ + "roads", + "road system" + ] + }, + { + "id": "16", + "solution": [ + "too late" + ] + } + ] } ] } \ No newline at end of file diff --git a/src/interfaces/exam.ts b/src/interfaces/exam.ts index 97c340d8..471ac3d3 100644 --- a/src/interfaces/exam.ts +++ b/src/interfaces/exam.ts @@ -16,7 +16,7 @@ export interface ListeningExam { exercises: Exercise[]; } -export type Exercise = FillBlanksExercise | MatchSentencesExercise | MultipleChoiceExercise; +export type Exercise = FillBlanksExercise | MatchSentencesExercise | MultipleChoiceExercise | WriteBlanksExercise; export interface FillBlanksExercise { prompt: string; // *EXAMPLE: "Complete the summary below. Click a blank to select the corresponding word for it." @@ -30,6 +30,17 @@ export interface FillBlanksExercise { }[]; } +export interface WriteBlanksExercise { + prompt: string; // *EXAMPLE: "Complete the notes below by writing NO MORE THAN THREE WORDS in the spaces provided." + maxWords: number; // *EXAMPLE: 3 - The maximum amount of words allowed per blank, 0 for unlimited + type: "writeBlanks"; + text: string; // *EXAMPLE: "The Government plans to give ${{14}}" + solutions: { + id: string; // *EXAMPLE: "14" + solution: string[]; // *EXAMPLE: ["Prescott"] - All possible solutions (case sensitive) + }[]; +} + export interface MatchSentencesExercise { type: "matchSentences"; prompt: string; diff --git a/yarn.lock b/yarn.lock index 8fa9348b..523ee7eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -716,6 +716,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deepmerge@^4.0.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -1689,6 +1694,11 @@ lilconfig@^2.0.5, lilconfig@^2.0.6: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== +load-script@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" + integrity sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA== + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -1720,6 +1730,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +memoize-one@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -2080,6 +2095,11 @@ react-dom@18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-fast-compare@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.1.tgz#53933d9e14f364281d6cba24bfed7a4afb808b5f" + integrity sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg== + react-is@^16.13.1, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -2093,6 +2113,17 @@ react-lineto@^3.3.0: prop-types "15.7.2" react "17.0.2" +react-player@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.12.0.tgz#2fc05dbfec234c829292fbca563b544064bd14f0" + integrity sha512-rymLRz/2GJJD+Wc01S7S+i9pGMFYnNmQibR2gVE3KmHJCBNN8BhPAlOPTGZtn1uKpJ6p4RPLlzPQ1OLreXd8gw== + dependencies: + deepmerge "^4.0.0" + load-script "^1.0.0" + memoize-one "^5.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.0.1" + react-string-replace@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-string-replace/-/react-string-replace-1.1.0.tgz#a3f7b458e697e77d70b0ea663caf38ab38f7cc17"