website

source code for the ttmaths.uk website
Log | Files | Refs | README

QuestionCard.jsx (2952B)


      1 import { useState } from 'preact/hooks';
      2 import QuestionList from './QuestionList.jsx';
      3 import Card from './Card.jsx';
      4 import { getRandomChoice, getRandomNumber } from '../scripts/randomUtils.js';
      5 
      6 const seedDiff = 100;
      7 
      8 function resolveParameters(question, variables) {
      9   const parameters = question.constants ?? { };
     10   const variableKeys = question.variables ?? { };
     11   Object.keys(variableKeys).forEach((key) => {
     12     parameters[key] = variables[variableKeys[key]];
     13   })
     14   return parameters;
     15 }
     16 
     17 function renderQuestion(seed, showAnswer, variables) {
     18   return (question, i) => {
     19     const QuestionComponent = question.type;
     20     const parameters = resolveParameters(question, variables);
     21     return <QuestionComponent key={i} seed={seed+seedDiff*i} showAnswer={showAnswer} {...parameters} />;
     22   };
     23 }
     24 
     25 function getQuestionList(id, questions, showAnswers, seed, variables) {
     26   return (
     27     <QuestionList key={id}>
     28       {questions.map(renderQuestion(seed, showAnswers, variables))}
     29     </QuestionList>
     30   );
     31 }
     32 
     33 function shuffleArray(arr, n, seed) {
     34   return Array.from({ length: n }).map(() => getRandomChoice(arr, ++seed));
     35 }
     36 
     37 export default function QuestionCard({ data }) {
     38 
     39   const dataVariables = data.variables ?? [];
     40 
     41   const defaultVariables = Object.fromEntries(
     42     dataVariables.map(input => [input.id, input.default ?? input.values])
     43   );
     44 
     45   const [showAnswers, setShowAnswers] = useState(false);
     46   const [seed       , setSeed       ] = useState(Math.random());
     47   const [variables  , setVariables  ] = useState(defaultVariables);
     48 
     49   function setVariable(id) {
     50     return (value) => {
     51       setVariables({
     52         ...variables,
     53         [id]: value
     54       });
     55     }
     56   }
     57 
     58   function revealAnswers() { setShowAnswers(!showAnswers); }
     59   function triggerRefresh() { setSeed(getRandomNumber(seed)); }
     60 
     61   let current_seed = seed;
     62   const questions = data.questionLists.map(questionList => {
     63     const { id: id, n: n, questions: unique_questions } = questionList;
     64     const questions = shuffleArray(unique_questions, n, current_seed);
     65     const randomQuestionList = getQuestionList(id, questions, showAnswers, current_seed, variables);
     66     current_seed += seedDiff * n;
     67     return randomQuestionList;
     68   });
     69 
     70   const fields = dataVariables.map(input => {
     71     const { type: InputComponent, ...props } = input;
     72     return <InputComponent key={input.id} variable={variables[input.id]} setVariable={setVariable(input.id)} {...props} />;
     73   });
     74 
     75   return (
     76     <Card title={data.title}>
     77       <div class="variables">
     78         {fields}
     79       </div>
     80       <fieldset>
     81         <legend>Display</legend>
     82         <button onClick={triggerRefresh}>
     83           Randomise questions
     84         </button>
     85         <button onClick={revealAnswers}>
     86           {showAnswers ? "Show questions" : "Show answers"}
     87         </button>
     88       </fieldset>
     89       <div class="print">
     90         <ol class="master-question-list">
     91           {questions}
     92         </ol>
     93       </div>
     94     </Card>
     95   );
     96 }