Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | 10x 10x 10x 10x 10x 10x 56x 10x 56x 7x 1x 7x 1x 7x 1x 7x 1x 7x 1x 4x 7x 28x 1x 1x 1x 1x | import update from 'immutability-helper'
import { ReactComponent as AddIcon } from '../../assets/svg/add.svg'
import { ReactComponent as CheckedIcon } from '../../assets/svg/checked.svg'
import { ReactComponent as DeleteIcon } from '../../assets/svg/delete.svg'
import { ReactNode } from 'react'
import { Choice, Question, createChoice } from 'sqc-core-functions'
import { Editor } from '../Editor'
export type QuizEditorParams = {
/** The quiz question object */
question: Question
/** Whether richtext mode is enabled */
richtextMode?: boolean
/** Function to handle any changes on the question, including adding/editing/removing answers */
onChange: (question: Question) => void
/** Warning message component to display */
warning?: ReactNode
}
const isAnswerTogglable = (answer: Choice) => answer.isCorrect
/**
* Renders a QuizEditor component with question, answers, and warning.
*/
export const QuizEditor = ({ question, richtextMode, onChange, warning }: QuizEditorParams) => {
const isAnswerNonRemovable = (answer: Choice) => question.choices.length < 2 || answer.isCorrect
const handleQuestionUpdate = (value: string) => {
onChange({
...question,
question: value,
})
}
const handleAnswerUpdate = (index: number, value: string) => {
onChange(update(question, { choices: { [index]: { answer: { $set: value } } } }))
}
const handleAddAnswer = (index: number) => {
onChange(
update(question, {
choices: {
$splice: [[index + 1, 0, createChoice()]],
},
})
)
}
const handleRemoveAnswer = (index: number) => {
onChange(update(question, { choices: { $splice: [[index, 1]] } }))
}
const handleToggleAnswer = (choice: Choice) => {
onChange(
update(question, {
choices: {
$apply: (x: Choice[]): Choice[] =>
x.map((c) => ({
...c,
isCorrect: c.id === choice.id,
})),
},
})
)
}
return (
<div key={question.id}>
<h2 className='sqc-mb-2 sqc-text-xl sqc-font-semibold'>Question:</h2>
<Editor value={question.question} onChange={handleQuestionUpdate} richtextMode={richtextMode} />
{warning}
<h2 className='sqc-my-2 sqc-text-xl sqc-font-semibold'>Answers:</h2>
{question.choices.map((choice, index) => (
<div key={choice.id}>
<h3 className='sqc-mb-2 sqc-font-semibold sqc-text-l'>
Answer {index + 1}: {choice.isCorrect && <span className='sqc-text-emerald-500'>Correct</span>}
</h3>
<div className='sqc-relative sqc-mb-6'>
<div className='sqc-absolute sqc-inset-y-0 sqc-left-0 sqc-flex sqc-flex-col sqc-items-center sqc-justify-center sqc-gap-2 sqc-pl-3'>
<button
type='button'
role='toggle-answer'
aria-disabled={isAnswerTogglable(choice)}
disabled={isAnswerTogglable(choice)}
className='sqc-text-slate-400 hover:sqc-rounded-lg hover:sqc-border hover:sqc-bg-emerald-200 disabled:sqc-cursor-not-allowed disabled:sqc-bg-transparent disabled:sqc-text-emerald-400'
onClick={() => handleToggleAnswer(choice)}
>
<CheckedIcon className='sqc-w-8 sqc-h-8' fill='currentColor' />
<span className='sqc-sr-only'>Mark as correct answer</span>
</button>
<button
type='button'
role='remove-answer'
aria-disabled={isAnswerNonRemovable(choice)}
disabled={isAnswerNonRemovable(choice)}
className='sqc-text-red-400 hover:sqc-rounded-lg hover:sqc-border hover:sqc-bg-red-200 disabled:sqc-cursor-not-allowed disabled:sqc-bg-transparent'
onClick={() => handleRemoveAnswer(index)}
>
<DeleteIcon className='sqc-w-8 sqc-h-8' fill='currentColor' />
<span className='sqc-sr-only'>Remove this answer</span>
</button>
<button
type='button'
role='add-answer'
onClick={() => handleAddAnswer(index)}
className='sqc-text-blue-400 hover:sqc-rounded-lg hover:sqc-border hover:sqc-bg-blue-200'
>
<AddIcon className='sqc-w-8 sqc-h-8' fill='currentColor' />
<span className='sqc-sr-only'>Add an answer after</span>
</button>
</div>
<div className='sqc-block sqc-w-full sqc-rounded-lg sqc-border sqc-border-gray-300 sqc-bg-gray-50 sqc-p-2.5 sqc-pl-14 lg:sqc-py-5'>
<Editor
value={choice.answer}
onChange={(val) => handleAnswerUpdate(index, val)}
richtextMode={richtextMode}
/>
{warning}
</div>
</div>
</div>
))}
</div>
)
}
|