@pocketprep/ui-kit 3.4.89 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@pocketprep/ui-kit.js +14834 -16036
- package/dist/@pocketprep/ui-kit.js.map +1 -1
- package/dist/@pocketprep/ui-kit.umd.cjs +12 -12
- package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/lib/components/Bundles/BundleSearch.vue +41 -11
- package/lib/components/Pagination/QuestionReviewPagination.vue +21 -19
- package/lib/components/Quiz/Question/ChoicesContainer.vue +95 -129
- package/lib/components/Quiz/Question/DropdownExplanation.vue +39 -53
- package/lib/components/Quiz/Question/Explanation.vue +47 -57
- package/lib/components/Quiz/Question/MatrixChoicesContainer.vue +211 -224
- package/lib/components/Quiz/Question/MatrixRadioGroup.vue +5 -4
- package/lib/components/Quiz/Question/MobileMatrixChoicesContainer.vue +321 -319
- package/lib/components/Quiz/Question/MobileMatrixRadioGroup.vue +7 -6
- package/lib/components/Quiz/Question/PassageAndImage.vue +32 -43
- package/lib/components/Quiz/Question/PassageAndImageDropdown.vue +32 -43
- package/lib/components/Quiz/Question/Paywall.vue +28 -39
- package/lib/components/Quiz/Question/QuestionContext.vue +23 -31
- package/lib/components/Quiz/Question/StatsSummary.vue +10 -20
- package/lib/components/Quiz/Question/Summary.vue +54 -64
- package/lib/components/Quiz/Question/composables.ts +71 -0
- package/lib/components/Quiz/Question/injectionSymbols.ts +69 -0
- package/lib/components/Quiz/Question.vue +788 -988
- package/lib/components/Quiz/QuizContainer.vue +36 -34
- package/lib/components/Quiz/question.d.ts +4 -4
- package/lib/directives.ts +27 -22
- package/lib/styles/_breakpoints.scss +6 -12
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
ref="question"
|
|
4
|
-
v-breakpoint="
|
|
3
|
+
ref="uikit-question"
|
|
4
|
+
v-breakpoint="breakpointsWithEl"
|
|
5
5
|
class="uikit-question"
|
|
6
6
|
:class="{
|
|
7
7
|
'uikit-question--show-side': (showExplanation && !showPaywall) || showPassageAndImage,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
>
|
|
10
10
|
<slot name="closeIcon">
|
|
11
11
|
<Icon
|
|
12
|
-
v-breakpoint:questionEl="
|
|
12
|
+
v-breakpoint:questionEl="breakpointsWithEl"
|
|
13
13
|
v-dark="isDarkMode"
|
|
14
14
|
class="uikit-question__close-icon"
|
|
15
15
|
role="button"
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
</slot>
|
|
21
21
|
<div
|
|
22
22
|
class="uikit-question__tag-mobile"
|
|
23
|
-
v-breakpoint
|
|
23
|
+
v-breakpoint="breakpointsWithEl"
|
|
24
24
|
>
|
|
25
25
|
<slot name="tag" />
|
|
26
26
|
</div>
|
|
27
27
|
<div
|
|
28
|
-
v-breakpoint
|
|
28
|
+
v-breakpoint="breakpointsWithEl"
|
|
29
29
|
class="uikit-question__main"
|
|
30
30
|
:class="{
|
|
31
31
|
'uikit-question__main--show-side': (showExplanation && !showPaywall) || showPassageAndImage,
|
|
@@ -43,19 +43,9 @@
|
|
|
43
43
|
<slot name="context">
|
|
44
44
|
<QuestionContext
|
|
45
45
|
ref="uikit-question__context"
|
|
46
|
-
v-breakpoint
|
|
46
|
+
v-breakpoint="breakpointsWithEl"
|
|
47
47
|
class="uikit-question__context"
|
|
48
48
|
tabindex="-1"
|
|
49
|
-
:quiz-length="quizLength"
|
|
50
|
-
:quiz-mode="quizMode"
|
|
51
|
-
:question-number="questionNumber"
|
|
52
|
-
:is-correct="isCorrect"
|
|
53
|
-
:is-dark-mode="isDarkMode"
|
|
54
|
-
:context-icon-type="contextIconType"
|
|
55
|
-
:show-answers="showAnswers"
|
|
56
|
-
:show-matrix-answers="showMatrixAnswers"
|
|
57
|
-
:question-el="questionEl"
|
|
58
|
-
:breakpoints="breakpoints"
|
|
59
49
|
>
|
|
60
50
|
<template #contextIcon>
|
|
61
51
|
<slot name="contextIcon" />
|
|
@@ -68,7 +58,7 @@
|
|
|
68
58
|
<div
|
|
69
59
|
v-if="questionScenario && currentScenarioQuestionNumber && numberOfScenarioQuestions"
|
|
70
60
|
v-dark="isDarkMode"
|
|
71
|
-
v-breakpoint
|
|
61
|
+
v-breakpoint="breakpointsWithEl"
|
|
72
62
|
tabindex="-1"
|
|
73
63
|
class="uikit-question__scenario-part-x-of-y-label"
|
|
74
64
|
:aria-label="`Scenario Part ${currentScenarioQuestionNumber} of ${numberOfScenarioQuestions}`"
|
|
@@ -78,8 +68,8 @@
|
|
|
78
68
|
</div>
|
|
79
69
|
<slot name="promptExperiment" />
|
|
80
70
|
<div
|
|
81
|
-
ref="
|
|
82
|
-
v-breakpoint
|
|
71
|
+
ref="promptRef"
|
|
72
|
+
v-breakpoint="breakpointsWithEl"
|
|
83
73
|
tabindex="-1"
|
|
84
74
|
role="group"
|
|
85
75
|
class="uikit-question__prompt"
|
|
@@ -91,7 +81,7 @@
|
|
|
91
81
|
/>
|
|
92
82
|
<PocketButton
|
|
93
83
|
v-if="question.passage || passageImageUrl"
|
|
94
|
-
v-breakpoint
|
|
84
|
+
v-breakpoint="breakpointsWithEl"
|
|
95
85
|
class="uikit-question__skip-to-passage"
|
|
96
86
|
@click="moveFocusToPassage"
|
|
97
87
|
>
|
|
@@ -102,26 +92,11 @@
|
|
|
102
92
|
ref="uikit-question__passage-and-image-dropdown"
|
|
103
93
|
class="uikit-question__passage-and-image-dropdown"
|
|
104
94
|
:class="{ 'uikit-question__passage-and-image-dropdown--review-mode': reviewMode }"
|
|
105
|
-
:question="question"
|
|
106
|
-
:question-el="questionEl"
|
|
107
|
-
:breakpoints="breakpoints"
|
|
108
|
-
:is-dark-mode="isDarkMode"
|
|
109
|
-
:review-mode="reviewMode"
|
|
110
|
-
:image-url-prefix="imageUrlPrefix"
|
|
111
|
-
:show-passage-image-long-alt="showPassageImageLongAlt"
|
|
112
|
-
:passage-image-url="passageImageUrl"
|
|
113
|
-
:passage-image-long-alt="passageImageLongAlt"
|
|
114
|
-
:passage-image-alt="passageImageAlt"
|
|
115
|
-
:passage-and-image-title="passageAndImageTitle"
|
|
116
95
|
@togglePassageImageLongAltDropdown="togglePassageImageLongAlt"
|
|
117
96
|
/>
|
|
118
97
|
<Paywall
|
|
119
98
|
v-if="showPaywall"
|
|
120
99
|
class="uikit-question__paywall"
|
|
121
|
-
:review-Mode="reviewMode"
|
|
122
|
-
:is-dark-mode="isDarkMode"
|
|
123
|
-
:question-el="questionEl"
|
|
124
|
-
:breakpoints="breakpoints"
|
|
125
100
|
@upgradeClicked="emitUpgrade"
|
|
126
101
|
/>
|
|
127
102
|
<div
|
|
@@ -175,33 +150,6 @@
|
|
|
175
150
|
<ChoicesContainer
|
|
176
151
|
v-if="question.type !== 'Matrix Checkbox' && question.type !== 'Matrix Radio Button'"
|
|
177
152
|
ref="uikit-question__choices-container"
|
|
178
|
-
:question="question"
|
|
179
|
-
:choices="choices"
|
|
180
|
-
:show-answers="showAnswers"
|
|
181
|
-
:show-explanation="showExplanation"
|
|
182
|
-
:is-MCR="isMCR"
|
|
183
|
-
:is-unanswered="isUnanswered"
|
|
184
|
-
:answer-keys="answerKeys"
|
|
185
|
-
:hover-choice-key="hoverChoiceKey"
|
|
186
|
-
:focus-choice-key="focusChoiceKey"
|
|
187
|
-
:selected-choices="selectedChoices"
|
|
188
|
-
:distractor-keys="distractorKeys"
|
|
189
|
-
:choice-strikes="choiceStrikes"
|
|
190
|
-
:choice-scores="choiceScores"
|
|
191
|
-
:passage-image-url="passageImageUrl"
|
|
192
|
-
:is-correct="isCorrect"
|
|
193
|
-
:global-metrics="globalMetrics"
|
|
194
|
-
:reviewMode="reviewMode"
|
|
195
|
-
:show-explanation-image-long-alt="showExplanationImageLongAlt"
|
|
196
|
-
:explanation-image-url="explanationImageUrl"
|
|
197
|
-
:explanation-image-alt="explanationImageAlt"
|
|
198
|
-
:explanation-image-long-alt="explanationImageLongAlt"
|
|
199
|
-
:reference="reference"
|
|
200
|
-
:hide-references="hideReferences"
|
|
201
|
-
:is-dark-mode="isDarkMode"
|
|
202
|
-
:question-el="questionEl"
|
|
203
|
-
:breakpoints="breakpoints"
|
|
204
|
-
:keyword-definitions="keywordDefinitions"
|
|
205
153
|
@emitChoiceMouseOver="choiceMouseOver"
|
|
206
154
|
@emitChoiceMouseLeave="choiceMouseLeave"
|
|
207
155
|
@emitChoiceFocusIn="choiceFocusIn"
|
|
@@ -248,21 +196,7 @@
|
|
|
248
196
|
v-if="question.type === 'Matrix Checkbox' || question.type === 'Matrix Radio Button'"
|
|
249
197
|
class="uikit-question__matrix-choices-container"
|
|
250
198
|
ref="uikit-question__matrix-choices-container"
|
|
251
|
-
v-breakpoint
|
|
252
|
-
:question="question"
|
|
253
|
-
:matrix-choice-layout="question.matrixChoiceLayout"
|
|
254
|
-
:is-matrix-question-correct="isMatrixQuestionCorrect"
|
|
255
|
-
:matrix-answer-keys="matrixAnswerKeys"
|
|
256
|
-
:matrix-distractor-keys="matrixDistractorKeys"
|
|
257
|
-
:selected-matrix-choices="selectedMatrixChoices"
|
|
258
|
-
:show-matrix-answers="showMatrixAnswers"
|
|
259
|
-
:review-mode="reviewMode"
|
|
260
|
-
:matrix-choice-scores="matrixChoiceScores"
|
|
261
|
-
:global-metrics="globalMetrics"
|
|
262
|
-
:is-dark-mode="isDarkMode"
|
|
263
|
-
:question-el="questionEl"
|
|
264
|
-
:breakpoints="breakpoints"
|
|
265
|
-
:is-teach-group-review="isTeachGroupReview"
|
|
199
|
+
v-breakpoint="breakpointsWithEl"
|
|
266
200
|
@emitSelectedMatrixChoice="selectMatrixChoice"
|
|
267
201
|
>
|
|
268
202
|
<template #motivationalMoment="{
|
|
@@ -286,19 +220,7 @@
|
|
|
286
220
|
<MobileMatrixChoicesContainer
|
|
287
221
|
v-if="question.type === 'Matrix Checkbox' || question.type === 'Matrix Radio Button'"
|
|
288
222
|
class="uikit-question__mobile-matrix-choices-container"
|
|
289
|
-
v-breakpoint
|
|
290
|
-
:question="question"
|
|
291
|
-
:matrix-choice-layout="question.matrixChoiceLayout"
|
|
292
|
-
:is-matrix-question-correct="isMatrixQuestionCorrect"
|
|
293
|
-
:matrix-answer-keys="matrixAnswerKeys"
|
|
294
|
-
:matrix-distractor-keys="matrixDistractorKeys"
|
|
295
|
-
:selected-matrix-choices="selectedMatrixChoices"
|
|
296
|
-
:show-matrix-answers="showMatrixAnswers"
|
|
297
|
-
:review-mode="reviewMode"
|
|
298
|
-
:global-metrics="globalMetrics"
|
|
299
|
-
:is-dark-mode="isDarkMode"
|
|
300
|
-
:question-el="questionEl"
|
|
301
|
-
:breakpoints="breakpoints"
|
|
223
|
+
v-breakpoint="breakpointsWithEl"
|
|
302
224
|
@emitSelectedMatrixChoice="selectMatrixChoice"
|
|
303
225
|
>
|
|
304
226
|
<template
|
|
@@ -326,23 +248,6 @@
|
|
|
326
248
|
v-if="((isMCR && showAnswers) || (isMatrixQuestion && showMatrixAnswers)) && !showPaywall"
|
|
327
249
|
ref="uikit-question__summary"
|
|
328
250
|
class="uikit-question__summary"
|
|
329
|
-
:question="question"
|
|
330
|
-
:show-explanation="showExplanation"
|
|
331
|
-
:show-explanation-image-long-alt="showExplanationImageLongAlt"
|
|
332
|
-
:explanation-image-url="explanationImageUrl"
|
|
333
|
-
:explanation-image-alt="explanationImageAlt"
|
|
334
|
-
:explanation-image-long-alt="explanationImageLongAlt"
|
|
335
|
-
:reference="reference"
|
|
336
|
-
:hide-references="hideReferences"
|
|
337
|
-
:is-correct="isCorrect"
|
|
338
|
-
:is-matrix-question-correct="isMatrixQuestionCorrect"
|
|
339
|
-
:is-matrix-question="isMatrixQuestion"
|
|
340
|
-
:is-unanswered="isUnanswered"
|
|
341
|
-
:review-mode="reviewMode"
|
|
342
|
-
:is-dark-mode="isDarkMode"
|
|
343
|
-
:question-el="questionEl"
|
|
344
|
-
:breakpoints="breakpoints"
|
|
345
|
-
:keyword-definitions="keywordDefinitions"
|
|
346
251
|
@click="keywordClick"
|
|
347
252
|
@toggleSummaryExplanation="toggleExplanation"
|
|
348
253
|
@toggleSummaryExplanationImageLongAlt="toggleExplanationImageLongAlt"
|
|
@@ -358,15 +263,10 @@
|
|
|
358
263
|
<StatsSummary
|
|
359
264
|
v-if="globalMetrics"
|
|
360
265
|
class="uikit-question__stats-summary"
|
|
361
|
-
:global-metrics="globalMetrics"
|
|
362
|
-
:choice-scores="choiceScores"
|
|
363
|
-
:matrix-choice-scores="matrixChoiceScores"
|
|
364
|
-
:is-matrix-question="isMatrixQuestion"
|
|
365
|
-
:is-dark-mode="isDarkMode"
|
|
366
266
|
/>
|
|
367
267
|
</slot>
|
|
368
268
|
<div
|
|
369
|
-
v-breakpoint
|
|
269
|
+
v-breakpoint="breakpointsWithEl"
|
|
370
270
|
class="uikit-question__motivational-moment-bottom"
|
|
371
271
|
:class="{
|
|
372
272
|
'uikit-question__motivational-moment-bottom--mcr': isMCR,
|
|
@@ -386,7 +286,7 @@
|
|
|
386
286
|
<slot name="actionExperiment" />
|
|
387
287
|
<div
|
|
388
288
|
v-if="!reviewMode"
|
|
389
|
-
v-breakpoint
|
|
289
|
+
v-breakpoint="breakpointsWithEl"
|
|
390
290
|
class="uikit-question__action-container"
|
|
391
291
|
:class="{
|
|
392
292
|
'uikit-question__action-container--mcr-summary': isMCR && showAnswers,
|
|
@@ -436,7 +336,7 @@
|
|
|
436
336
|
</div>
|
|
437
337
|
<div
|
|
438
338
|
v-if="showPassageAndImage || (showExplanation && !showPaywall)"
|
|
439
|
-
v-breakpoint
|
|
339
|
+
v-breakpoint="breakpointsWithEl"
|
|
440
340
|
class="uikit-question__right-side"
|
|
441
341
|
:class="{
|
|
442
342
|
'uikit-question__right-side--explanation': showExplanation && !showPaywall,
|
|
@@ -447,36 +347,12 @@
|
|
|
447
347
|
v-if="showPassageAndImage"
|
|
448
348
|
ref="uikit-question__passage-and-image"
|
|
449
349
|
class="uikit-question__passage-and-image"
|
|
450
|
-
:question="question"
|
|
451
|
-
:show-passage-image-long-alt="showPassageImageLongAlt"
|
|
452
|
-
:show-passage-and-image="showPassageAndImage"
|
|
453
|
-
:passage-image-url="passageImageUrl"
|
|
454
|
-
:passage-image-long-alt="passageImageLongAlt"
|
|
455
|
-
:passage-image-alt="passageImageAlt"
|
|
456
|
-
:passage-and-image-title="passageAndImageTitle"
|
|
457
|
-
:is-dark-mode="isDarkMode"
|
|
458
|
-
:question-el="questionEl"
|
|
459
|
-
:breakpoints="breakpoints"
|
|
460
350
|
@emitTogglePassageImageLongAlt="togglePassageImageLongAlt"
|
|
461
351
|
@emitMoveFocusToPrompt="moveFocusToPrompt"
|
|
462
352
|
/>
|
|
463
353
|
<Explanation
|
|
464
354
|
ref="uikit-question__explanation"
|
|
465
355
|
class="uikit-question__explanation"
|
|
466
|
-
:question="question"
|
|
467
|
-
:show-explanation="showExplanation"
|
|
468
|
-
:show-paywall="showPaywall"
|
|
469
|
-
:review-mode="reviewMode"
|
|
470
|
-
:show-explanation-image-long-alt="showExplanationImageLongAlt"
|
|
471
|
-
:explanation-image-url="explanationImageUrl"
|
|
472
|
-
:explanation-image-alt="explanationImageAlt"
|
|
473
|
-
:explanation-image-long-alt="explanationImageLongAlt"
|
|
474
|
-
:reference="reference"
|
|
475
|
-
:hide-references="hideReferences"
|
|
476
|
-
:is-dark-mode="isDarkMode"
|
|
477
|
-
:question-el="questionEl"
|
|
478
|
-
:breakpoints="breakpoints"
|
|
479
|
-
:keyword-definitions="keywordDefinitions"
|
|
480
356
|
@toggleExplanationImageLongAlt="toggleExplanationImageLongAlt"
|
|
481
357
|
@toggleExplanation="toggleExplanation"
|
|
482
358
|
@click="keywordClick"
|
|
@@ -492,17 +368,12 @@
|
|
|
492
368
|
</div>
|
|
493
369
|
</template>
|
|
494
370
|
|
|
495
|
-
<script lang="ts">
|
|
496
|
-
import {
|
|
497
|
-
import type { ComponentPublicInstance } from 'vue'
|
|
371
|
+
<script setup lang="ts">
|
|
372
|
+
import { computed, onMounted, onBeforeUnmount, watch, useTemplateRef, ref, provide } from 'vue'
|
|
498
373
|
import PocketButton from '../Buttons/Button.vue'
|
|
499
374
|
import Icon from '../Icons/Icon.vue'
|
|
500
|
-
import type { ITableColumn, ITableSortSettings } from '../Tables/table'
|
|
501
|
-
import Table from '../Tables/Table.vue'
|
|
502
|
-
import OverflowTooltip from '../Tooltips/OverflowTooltip.vue'
|
|
503
375
|
import QuestionContext from '../Quiz/Question/QuestionContext.vue'
|
|
504
376
|
import PassageAndImageDropdown from '../Quiz/Question/PassageAndImageDropdown.vue'
|
|
505
|
-
import DropdownExplanation from '../Quiz/Question/DropdownExplanation.vue'
|
|
506
377
|
import Paywall from '../Quiz/Question/Paywall.vue'
|
|
507
378
|
import Summary from '../Quiz/Question/Summary.vue'
|
|
508
379
|
import ChoicesContainer from '../Quiz/Question/ChoicesContainer.vue'
|
|
@@ -512,7 +383,6 @@ import PassageAndImage from '../Quiz/Question/PassageAndImage.vue'
|
|
|
512
383
|
import MatrixChoicesContainer from '../Quiz/Question/MatrixChoicesContainer.vue'
|
|
513
384
|
import MobileMatrixChoicesContainer from '../Quiz/Question/MobileMatrixChoicesContainer.vue'
|
|
514
385
|
import type { Study } from '@pocketprep/types'
|
|
515
|
-
import { breakpoint, dark } from '../../directives'
|
|
516
386
|
import { highlightKeywordsInText, studyModes } from '../../utils'
|
|
517
387
|
import type {
|
|
518
388
|
Ref,
|
|
@@ -522,996 +392,926 @@ import type {
|
|
|
522
392
|
TChoice,
|
|
523
393
|
TChoiceScores,
|
|
524
394
|
TMatrixChoiceScores,
|
|
525
|
-
TNamesRow,
|
|
526
395
|
TViewNames,
|
|
527
396
|
IScenarioSerial,
|
|
528
397
|
} from './question'
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
398
|
+
import { dark as vDark, breakpoint as vBreakpoint } from '../../directives'
|
|
399
|
+
import * as InjectionKeys from './Question/injectionSymbols'
|
|
400
|
+
|
|
401
|
+
const props = withDefaults(defineProps<{
|
|
402
|
+
question: Study.Class.QuestionJSON
|
|
403
|
+
questionNumber: number
|
|
404
|
+
quizLength: number
|
|
405
|
+
imageUrlPrefix?: string
|
|
406
|
+
quizMode?: TQuizMode
|
|
407
|
+
initialShowAnswers?: boolean
|
|
408
|
+
showCheckAnswer?: boolean
|
|
409
|
+
hideAnswer?: boolean
|
|
410
|
+
showNextQuestion?: boolean
|
|
411
|
+
reviewMode?: boolean
|
|
412
|
+
previousChoices?: TChoiceKey[] | null
|
|
413
|
+
previousMatrixChoices?: TMatrixChoiceKey[] | null
|
|
414
|
+
globalMetrics?: Study.Class.GlobalQuestionMetricJSON | null
|
|
415
|
+
showNames?: TViewNames | null
|
|
416
|
+
allowKeyboardShortcuts?: boolean
|
|
417
|
+
containerEl?: HTMLElement | null
|
|
418
|
+
isDarkMode?: boolean
|
|
419
|
+
answerSeed?: number[] | null
|
|
420
|
+
showCloseButton?: boolean
|
|
421
|
+
defaultShowExplanation?: boolean | null
|
|
422
|
+
autoFocusPrompt?: boolean | null // false autofocuses the context, null is no autofocus
|
|
423
|
+
showPaywall?: boolean
|
|
424
|
+
hideReferences?: boolean
|
|
425
|
+
isTeachReview?: boolean
|
|
426
|
+
isTeachGroupReview?: boolean
|
|
427
|
+
keywordDefinitions: { keyword: string; definition: string }[]
|
|
428
|
+
}>(), {
|
|
429
|
+
imageUrlPrefix: '',
|
|
430
|
+
quizMode: 'quick10',
|
|
431
|
+
initialShowAnswers: false,
|
|
432
|
+
showCheckAnswer: true,
|
|
433
|
+
hideAnswer: false,
|
|
434
|
+
showNextQuestion: true,
|
|
435
|
+
reviewMode: false,
|
|
436
|
+
previousChoices: null,
|
|
437
|
+
previousMatrixChoices: null,
|
|
438
|
+
globalMetrics: null,
|
|
439
|
+
showNames: null,
|
|
440
|
+
allowKeyboardShortcuts: true,
|
|
441
|
+
containerEl: null,
|
|
442
|
+
isDarkMode: false,
|
|
443
|
+
answerSeed: null,
|
|
444
|
+
showCloseButton: false,
|
|
445
|
+
defaultShowExplanation: null,
|
|
446
|
+
autoFocusPrompt: false,
|
|
447
|
+
showPaywall: false,
|
|
448
|
+
hideReferences: false,
|
|
449
|
+
isTeachReview: false,
|
|
450
|
+
isTeachGroupReview: false,
|
|
451
|
+
keywordDefinitions: () => [],
|
|
552
452
|
})
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
453
|
+
|
|
454
|
+
const emit = defineEmits<{
|
|
455
|
+
'keyword-click': [ keywordClick: {
|
|
456
|
+
keyword: string
|
|
457
|
+
location: string | null
|
|
458
|
+
clickLocation: {
|
|
459
|
+
x: number
|
|
460
|
+
y: number
|
|
461
|
+
}
|
|
462
|
+
target: HTMLElement
|
|
463
|
+
} | undefined ]
|
|
464
|
+
'selectedChoices': [ selectedChoices: Study.Cloud.IQuizAnswer ]
|
|
465
|
+
'close': []
|
|
466
|
+
'upgrade': []
|
|
467
|
+
'submitQuiz': []
|
|
468
|
+
'nextQuestion': []
|
|
469
|
+
'previousQuestion': []
|
|
470
|
+
'checkAnswer': [ answer: Study.Cloud.IQuizAnswer ]
|
|
471
|
+
'update:showExplanation': [ showExplanation: boolean ]
|
|
472
|
+
}>()
|
|
473
|
+
|
|
474
|
+
const question = computed(() => props.question)
|
|
475
|
+
const quizLength = computed(() => props.quizLength)
|
|
476
|
+
const isDarkMode = computed(() => props.isDarkMode)
|
|
477
|
+
const quizMode = computed(() => props.quizMode)
|
|
478
|
+
const questionNumber = computed(() => props.questionNumber)
|
|
479
|
+
const imageUrlPrefix = computed(() => props.imageUrlPrefix)
|
|
480
|
+
const globalMetrics = computed(() => props.globalMetrics)
|
|
481
|
+
const reviewMode = computed(() => props.reviewMode)
|
|
482
|
+
const hideReferences = computed(() => props.hideReferences)
|
|
483
|
+
const keywordDefinitions = computed(() => props.keywordDefinitions)
|
|
484
|
+
const showPaywall = computed(() => props.showPaywall)
|
|
485
|
+
const isTeachGroupReview = computed(() => props.isTeachGroupReview)
|
|
486
|
+
const hoverChoiceKey = ref<TChoiceKey | null>(null)
|
|
487
|
+
const focusChoiceKey = ref<TChoiceKey | null>(null)
|
|
488
|
+
const choiceStrikes = ref<TChoiceKey[]>([])
|
|
489
|
+
const selectedChoices = ref<TChoiceKey[]>([])
|
|
490
|
+
const selectedMatrixChoices = ref<TMatrixChoiceKey[]>([])
|
|
491
|
+
const showAnswers = ref(false)
|
|
492
|
+
const showMatrixAnswers = ref(false)
|
|
493
|
+
const showExplanation = ref(false)
|
|
494
|
+
const showPassageImageLongAlt = ref(false)
|
|
495
|
+
const showExplanationImageLongAlt = ref(false)
|
|
496
|
+
const swipeStart = ref<{
|
|
497
|
+
x: number | null
|
|
498
|
+
y: number | null
|
|
499
|
+
}>({ x: null, y: null })
|
|
500
|
+
const questionEl = ref<Element | null>(null)
|
|
501
|
+
const questionRef = useTemplateRef<HTMLElement>('uikit-question')
|
|
502
|
+
const questionContextRef = useTemplateRef<{ contextEl?: HTMLElement }>('uikit-question__context')
|
|
503
|
+
const promptEl = useTemplateRef<HTMLElement>('promptRef')
|
|
504
|
+
const passageAndImageRef = useTemplateRef<{
|
|
505
|
+
passageTitleEl?: HTMLElement
|
|
506
|
+
longAltEl?: HTMLElement
|
|
507
|
+
}>('uikit-question__passage-and-image')
|
|
508
|
+
const choicesContainerRef = useTemplateRef<{
|
|
509
|
+
choiceEls?: HTMLElement[]
|
|
510
|
+
mobileImgDropdownImgDescriptionEl?: HTMLElement
|
|
511
|
+
showExplanationEls?: HTMLElement[] | HTMLElement
|
|
512
|
+
}>('uikit-question__choices-container')
|
|
513
|
+
const passageImageDropdownRef = useTemplateRef<{
|
|
514
|
+
mobileImgDropdownImgDescriptionEl?: HTMLElement
|
|
515
|
+
}>('uikit-question__passage-and-image-dropdown')
|
|
516
|
+
const questionSummaryRef = useTemplateRef<{
|
|
517
|
+
mcrLongAltEl?: HTMLElement
|
|
518
|
+
summaryMCRExplanationEl?: HTMLElement
|
|
519
|
+
summaryMatrixExplanationEl?: HTMLElement
|
|
520
|
+
}>('uikit-question__summary')
|
|
521
|
+
const questionExplanationRef = useTemplateRef<{
|
|
522
|
+
explanationTitleEl?: HTMLElement
|
|
523
|
+
longAltEl?: HTMLElement
|
|
524
|
+
}>('uikit-question__explanation')
|
|
525
|
+
|
|
526
|
+
const breakpointsWithEl = computed(() => {
|
|
527
|
+
// Passing the container element (typically QuizContainer) allows us to bind to that element's width
|
|
528
|
+
if (props.containerEl) {
|
|
529
|
+
return {
|
|
530
|
+
breakpoints: {
|
|
616
531
|
'mobile': 767,
|
|
617
532
|
'tablet-portrait': 1023,
|
|
618
533
|
'tablet-landscape': 1439,
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
534
|
+
},
|
|
535
|
+
containerEl: props.containerEl,
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
// These were calculated to account for the expected widths of our Quiz Container sides
|
|
539
|
+
return {
|
|
540
|
+
breakpoints: {
|
|
623
541
|
'mobile': 729, // 767 - (19 + 19)
|
|
624
542
|
'tablet-portrait': 978, // 1023 - (22 + 23)
|
|
625
543
|
'tablet-landscape': 1309, // 1439 - (65 + 65)
|
|
626
|
-
}
|
|
544
|
+
},
|
|
545
|
+
containerEl: null,
|
|
627
546
|
}
|
|
628
547
|
}
|
|
548
|
+
})
|
|
629
549
|
|
|
630
|
-
get questionEl () {
|
|
631
|
-
return this.containerEl || this.$refs['question']
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
get contextIconType () {
|
|
635
|
-
const mode = this.quizMode
|
|
636
|
-
return Object.values(studyModes).find(studyMode => studyMode.shortName === mode)?.icon || 'quick10'
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
get isMCR () {
|
|
640
|
-
return this.question.type === 'Multiple Correct Response'
|
|
641
|
-
}
|
|
642
550
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
551
|
+
const contextIconType = computed(() => {
|
|
552
|
+
const mode = props.quizMode
|
|
553
|
+
return Object.values(studyModes).find(studyMode => studyMode.shortName === mode)?.icon || 'quick10'
|
|
554
|
+
})
|
|
646
555
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
556
|
+
const isMCR = computed(() => {
|
|
557
|
+
return props.question.type === 'Multiple Correct Response'
|
|
558
|
+
})
|
|
650
559
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
560
|
+
const isMatrixQuestion = computed(() => {
|
|
561
|
+
return props.question.type === 'Matrix Checkbox' || props.question.type === 'Matrix Radio Button'
|
|
562
|
+
})
|
|
654
563
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
(scenarioSerial: IScenarioSerial) => {
|
|
659
|
-
return scenarioSerial.serial === this.question.serial
|
|
660
|
-
}
|
|
661
|
-
)
|
|
564
|
+
const questionScenario = computed(() => {
|
|
565
|
+
return props.question.questionScenario as Study.Class.QuestionScenarioJSON | undefined
|
|
566
|
+
})
|
|
662
567
|
|
|
663
|
-
|
|
664
|
-
|
|
568
|
+
const numberOfScenarioQuestions = computed(() => {
|
|
569
|
+
return questionScenario.value?.questions.length
|
|
570
|
+
})
|
|
665
571
|
|
|
666
|
-
|
|
572
|
+
const currentScenarioQuestionNumber = computed(() => {
|
|
573
|
+
if (questionScenario.value?.questions) {
|
|
574
|
+
const indexOfScenarioSerial = questionScenario.value.questions.findIndex(
|
|
575
|
+
(scenarioSerial: IScenarioSerial) => {
|
|
576
|
+
return scenarioSerial.serial === props.question.serial
|
|
577
|
+
}
|
|
578
|
+
)
|
|
667
579
|
|
|
580
|
+
return indexOfScenarioSerial !== -1 ? indexOfScenarioSerial + 1 : undefined
|
|
668
581
|
}
|
|
582
|
+
return undefined
|
|
583
|
+
})
|
|
669
584
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
585
|
+
const passageImageUrl = computed(() => {
|
|
586
|
+
const imageUrl = props.question.passageImage?.url
|
|
587
|
+
|
|
588
|
+
return imageUrl ? `${props.imageUrlPrefix}${imageUrl}` : null
|
|
589
|
+
})
|
|
675
590
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
591
|
+
const passageImageAlt = computed(() => {
|
|
592
|
+
return props.question.passageImage?.altText
|
|
593
|
+
})
|
|
679
594
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
595
|
+
const passageImageLongAlt = computed(() => {
|
|
596
|
+
return props.question.passageImage?.longAltText
|
|
597
|
+
})
|
|
683
598
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
599
|
+
const explanationImageUrl = computed(() => {
|
|
600
|
+
const imageUrl = props.question.explanationImage?.url
|
|
601
|
+
|
|
602
|
+
return imageUrl ? `${props.imageUrlPrefix}${imageUrl}` : null
|
|
603
|
+
})
|
|
689
604
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
605
|
+
const explanationImageAlt = computed(() => {
|
|
606
|
+
return props.question.explanationImage?.altText
|
|
607
|
+
})
|
|
693
608
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
609
|
+
const explanationImageLongAlt = computed(() => {
|
|
610
|
+
return props.question.explanationImage?.longAltText
|
|
611
|
+
})
|
|
697
612
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
613
|
+
const showPassageAndImage = computed(() => {
|
|
614
|
+
return !showExplanation.value && !!(props.question.passage || passageImageUrl.value)
|
|
615
|
+
})
|
|
701
616
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
617
|
+
const passageAndImageTitle = computed(() => {
|
|
618
|
+
if (props.question.passage && passageImageUrl.value) {
|
|
619
|
+
return props.question.passageLabel ? `${props.question.passageLabel} + Image` :
|
|
620
|
+
'Passage + Image'
|
|
621
|
+
} else if (!props.question.passage && passageImageUrl.value) {
|
|
622
|
+
return 'Image'
|
|
623
|
+
} else {
|
|
624
|
+
return props.question.passageLabel ? `${props.question.passageLabel}` :
|
|
625
|
+
'Passage'
|
|
712
626
|
}
|
|
627
|
+
})
|
|
713
628
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
629
|
+
const reference = computed(() => {
|
|
630
|
+
return props.question.references?.length ? props.question.references.join('') : undefined
|
|
631
|
+
})
|
|
717
632
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
return answers.filter(choice => !!choice.text)
|
|
725
|
-
}
|
|
633
|
+
const answers = computed((): TChoice[] => {
|
|
634
|
+
return props.question.choices.filter(choice => choice.isCorrect).map((choice, index) => ({
|
|
635
|
+
text: choice.text,
|
|
636
|
+
key: `a${index + 1}` as `a${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`,
|
|
637
|
+
})).filter(choice => !!choice.text)
|
|
638
|
+
})
|
|
726
639
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
640
|
+
const answerKeys = computed((): TChoiceKey[] => {
|
|
641
|
+
return answers.value.map(choice => choice.key)
|
|
642
|
+
})
|
|
730
643
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
735
|
-
return []
|
|
644
|
+
const matrixAnswerKeys = computed<TMatrixChoiceKey[]>(() => {
|
|
645
|
+
if (props.question?.matrixChoiceLayout) {
|
|
646
|
+
return props.question?.matrixChoiceLayout.flat().filter(choice => choice.startsWith('a')) as TMatrixChoiceKey[]
|
|
736
647
|
}
|
|
648
|
+
return [] as TMatrixChoiceKey[]
|
|
649
|
+
})
|
|
737
650
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
|
|
651
|
+
const distractors = computed((): TChoice[] => {
|
|
652
|
+
return props.question.choices.filter(choice => !choice.isCorrect).map((choice, index) => ({
|
|
653
|
+
text: choice.text,
|
|
654
|
+
key: `d${index + 1}` as `d${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`,
|
|
655
|
+
})).filter(choice => !!choice.text)
|
|
656
|
+
})
|
|
744
657
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
key: `d${index + 1}` as `d${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`,
|
|
749
|
-
}))
|
|
658
|
+
const distractorKeys = computed((): TChoiceKey[] => {
|
|
659
|
+
return distractors.value.map(choice => choice.key)
|
|
660
|
+
})
|
|
750
661
|
|
|
751
|
-
|
|
752
|
-
|
|
662
|
+
const choices = computed(() => {
|
|
663
|
+
return shuffleChoices([
|
|
664
|
+
...answers.value,
|
|
665
|
+
...distractors.value,
|
|
666
|
+
])
|
|
667
|
+
})
|
|
753
668
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
669
|
+
const isCorrect = computed(() => {
|
|
670
|
+
// In order to be correct, user must have selected all the answers and none of the distractors
|
|
671
|
+
return showAnswers.value
|
|
672
|
+
&& selectedChoices.value.length === answerKeys.value.length
|
|
673
|
+
&& !selectedChoices.value.join(' ').includes('d')
|
|
674
|
+
})
|
|
757
675
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
}
|
|
676
|
+
const isMatrixQuestionCorrect = computed(() => {
|
|
677
|
+
return showMatrixAnswers.value
|
|
678
|
+
&& selectedMatrixChoices.value.length === matrixAnswerKeys.value.length
|
|
679
|
+
&& !selectedMatrixChoices.value.join(' ').includes('d')
|
|
680
|
+
})
|
|
764
681
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
682
|
+
const choiceScores = computed((): TChoiceScores => {
|
|
683
|
+
const metrics = globalMetrics.value
|
|
684
|
+
const scores: TChoiceScores = {
|
|
685
|
+
totalAnswered: selectedChoices.value.length && showAnswers && !props.reviewMode ? 1 : 0,
|
|
686
|
+
answeredCorrectly: isCorrect.value && !props.reviewMode ? 1 : 0,
|
|
770
687
|
}
|
|
771
688
|
|
|
772
|
-
|
|
773
|
-
return
|
|
774
|
-
&& this.selectedMatrixChoices.length === this.matrixAnswerKeys.length
|
|
775
|
-
&& !this.selectedMatrixChoices.join(' ').includes('d')
|
|
689
|
+
if (!metrics) {
|
|
690
|
+
return scores
|
|
776
691
|
}
|
|
777
692
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
answeredCorrectly: this.isCorrect && !this.reviewMode ? 1 : 0,
|
|
784
|
-
}
|
|
693
|
+
scores.totalAnswered += (
|
|
694
|
+
(metrics.answeredCorrectlyCount || 0)
|
|
695
|
+
+ (metrics.answeredIncorrectlyCount || 0)
|
|
696
|
+
)
|
|
697
|
+
scores.answeredCorrectly += (metrics.answeredCorrectlyCount || 0)
|
|
785
698
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
scores.totalAnswered += (
|
|
791
|
-
(globalMetrics.answeredCorrectlyCount || 0)
|
|
792
|
-
+ (globalMetrics.answeredIncorrectlyCount || 0)
|
|
793
|
-
)
|
|
794
|
-
scores.answeredCorrectly += (globalMetrics.answeredCorrectlyCount || 0)
|
|
795
|
-
|
|
796
|
-
const selectedChoicesCount = this.showAnswers && !this.reviewMode && this.selectedChoices.length || 0
|
|
797
|
-
const totalChoicesCount = selectedChoicesCount + Object.values(globalMetrics.choiceStats)
|
|
798
|
-
.reduce<number>((choiceCount, acc) => (acc || 0) + (choiceCount || 0), 0)
|
|
799
|
-
|
|
800
|
-
this.choices.forEach(({ key }) => {
|
|
801
|
-
const globalAnswerCount = globalMetrics.choiceStats[key] || 0
|
|
802
|
-
const userAnswerCount = this.showAnswers && this.selectedChoices.includes(key) && !this.reviewMode ? 1 : 0
|
|
803
|
-
const choiceAnswerCount = globalAnswerCount + userAnswerCount
|
|
804
|
-
const answerPct = Math.round((choiceAnswerCount / totalChoicesCount) * 100)
|
|
805
|
-
// In case we have NaN here when nobody answered the question and nobody got it right
|
|
806
|
-
scores[key] = answerPct || 0
|
|
807
|
-
})
|
|
808
|
-
|
|
809
|
-
return scores
|
|
810
|
-
}
|
|
699
|
+
const selectedChoicesCount = showAnswers.value && !props.reviewMode && selectedChoices.value.length || 0
|
|
700
|
+
const totalChoicesCount = selectedChoicesCount + Object.values(metrics.choiceStats)
|
|
701
|
+
.reduce<number>((choiceCount, acc) => (acc || 0) + (choiceCount || 0), 0)
|
|
811
702
|
|
|
812
|
-
|
|
813
|
-
const
|
|
703
|
+
choices.value.forEach(({ key }) => {
|
|
704
|
+
const globalAnswerCount = metrics.choiceStats[key] || 0
|
|
705
|
+
const userAnswerCount = showAnswers.value && selectedChoices.value.includes(key) && !props.reviewMode ? 1 : 0
|
|
706
|
+
const choiceAnswerCount = globalAnswerCount + userAnswerCount
|
|
707
|
+
const answerPct = Math.round((choiceAnswerCount / totalChoicesCount) * 100)
|
|
708
|
+
// In case we have NaN here when nobody answered the question and nobody got it right
|
|
709
|
+
scores[key] = answerPct || 0
|
|
710
|
+
})
|
|
814
711
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
this.showMatrixAnswers && !this.reviewMode ? 1 : 0,
|
|
818
|
-
answeredCorrectly: this.isCorrect && !this.reviewMode ? 1 : 0,
|
|
819
|
-
}
|
|
712
|
+
return scores
|
|
713
|
+
})
|
|
820
714
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
715
|
+
const matrixChoiceScores = computed((): TMatrixChoiceScores => {
|
|
716
|
+
const metrics = props.globalMetrics
|
|
824
717
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
scores.answeredCorrectly += (globalMetrics.answeredCorrectlyCount || 0)
|
|
830
|
-
return scores
|
|
718
|
+
const scores: TMatrixChoiceScores = {
|
|
719
|
+
totalAnswered: selectedMatrixChoices.value.length &&
|
|
720
|
+
showMatrixAnswers && !props.reviewMode ? 1 : 0,
|
|
721
|
+
answeredCorrectly: isCorrect.value && !props.reviewMode ? 1 : 0,
|
|
831
722
|
}
|
|
832
723
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
if (this.isMatrixQuestion) {
|
|
836
|
-
const answeredRowNums = this.selectedMatrixChoices.map(choice => {
|
|
837
|
-
if (choice as TMatrixChoiceKey) {
|
|
838
|
-
return Number(choice.split('_')[0]?.slice(1))
|
|
839
|
-
}
|
|
840
|
-
return
|
|
841
|
-
})
|
|
842
|
-
|
|
843
|
-
const matrixRows = this.question.matrixChoiceLayout || []
|
|
844
|
-
const isEveryRowAnswered = matrixRows.every((row, index) => {
|
|
845
|
-
const rowNum = index + 1
|
|
846
|
-
const hasSelectedChoiceForRow = answeredRowNums.includes(rowNum)
|
|
847
|
-
return hasSelectedChoiceForRow
|
|
848
|
-
})
|
|
849
|
-
return isEveryRowAnswered
|
|
850
|
-
}
|
|
851
|
-
return false
|
|
724
|
+
if (!metrics) {
|
|
725
|
+
return scores
|
|
852
726
|
}
|
|
853
727
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
728
|
+
scores.totalAnswered += (
|
|
729
|
+
(metrics.answeredCorrectlyCount || 0)
|
|
730
|
+
+ (metrics.answeredIncorrectlyCount || 0)
|
|
731
|
+
)
|
|
732
|
+
scores.answeredCorrectly += (metrics.answeredCorrectlyCount || 0)
|
|
733
|
+
return scores
|
|
734
|
+
})
|
|
858
735
|
|
|
859
|
-
|
|
860
|
-
|
|
736
|
+
const isMatrixQuestionAnswered = computed(() => {
|
|
737
|
+
// Matrix questions are answered if each row has a selected choice
|
|
738
|
+
if (isMatrixQuestion.value) {
|
|
739
|
+
const answeredRowNums = selectedMatrixChoices.value.map(choice => {
|
|
740
|
+
if (choice as TMatrixChoiceKey) {
|
|
741
|
+
return Number(choice.split('_')[0]?.slice(1))
|
|
742
|
+
}
|
|
743
|
+
return
|
|
744
|
+
})
|
|
861
745
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
location: 'prompt',
|
|
746
|
+
const matrixRows = props.question.matrixChoiceLayout || []
|
|
747
|
+
const isEveryRowAnswered = matrixRows.every((row, index) => {
|
|
748
|
+
const rowNum = index + 1
|
|
749
|
+
const hasSelectedChoiceForRow = answeredRowNums.includes(rowNum)
|
|
750
|
+
return hasSelectedChoiceForRow
|
|
868
751
|
})
|
|
752
|
+
return isEveryRowAnswered
|
|
869
753
|
}
|
|
754
|
+
return false
|
|
755
|
+
})
|
|
870
756
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
if (!this.isMatrixQuestion && this.previousChoices) {
|
|
877
|
-
this.updateSelectedChoices(this.previousChoices)
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (this.isMatrixQuestion && this.previousMatrixChoices) {
|
|
881
|
-
this.updateSelectedMatrixChoices(this.previousMatrixChoices)
|
|
882
|
-
}
|
|
757
|
+
const isUnanswered = computed(() => {
|
|
758
|
+
if (!isMatrixQuestion.value) {
|
|
759
|
+
return selectedChoices.value.length === 0
|
|
883
760
|
}
|
|
884
761
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
this.showAnswers = this.initialShowAnswers
|
|
888
|
-
this.showMatrixAnswers = this.initialShowAnswers
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
if (this.allowKeyboardShortcuts) {
|
|
892
|
-
window.addEventListener('keydown', this.keydownListener)
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
if (this.autoFocusPrompt) {
|
|
896
|
-
this.moveFocusToPrompt()
|
|
897
|
-
} else if (this.autoFocusPrompt === false) {
|
|
898
|
-
setTimeout(() => {
|
|
899
|
-
const contextComp = this.$refs['uikit-question__context'] as ComponentPublicInstance | undefined
|
|
900
|
-
const contextEl = contextComp?.$refs['uikit-question-context'] as HTMLElement | undefined
|
|
901
|
-
contextEl?.focus()
|
|
902
|
-
}, 0)
|
|
903
|
-
}
|
|
904
|
-
this.sortSettings = {
|
|
905
|
-
column: this.showNamesColumns[0] || null,
|
|
906
|
-
direction: 1,
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
const prompt = this.$refs['prompt'] as HTMLElement
|
|
910
|
-
const promptElements = prompt?.querySelectorAll('p')
|
|
911
|
-
if (promptElements.length) {
|
|
912
|
-
promptElements.forEach(el => el.setAttribute('tabindex', '0'))
|
|
913
|
-
}
|
|
914
|
-
}
|
|
762
|
+
return !isMatrixQuestionAnswered.value
|
|
763
|
+
})
|
|
915
764
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
765
|
+
const prompt = computed(() => {
|
|
766
|
+
return highlightKeywordsInText({
|
|
767
|
+
text: props.question.prompt,
|
|
768
|
+
keywordDefinitions: props.keywordDefinitions,
|
|
769
|
+
isDarkMode: props.isDarkMode,
|
|
770
|
+
location: 'prompt',
|
|
771
|
+
})
|
|
772
|
+
})
|
|
919
773
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
774
|
+
const keydownListener = (e: KeyboardEvent) => {
|
|
775
|
+
switch (e.code) {
|
|
776
|
+
case 'KeyA':
|
|
777
|
+
choices.value[0] && selectChoice(choices.value[0].key, true)
|
|
778
|
+
break
|
|
779
|
+
case 'KeyB':
|
|
780
|
+
choices.value[1] && selectChoice(choices.value[1].key, true)
|
|
781
|
+
break
|
|
782
|
+
case 'KeyC':
|
|
783
|
+
choices.value[2] && selectChoice(choices.value[2].key, true)
|
|
784
|
+
break
|
|
785
|
+
case 'KeyD':
|
|
786
|
+
choices.value[3] && selectChoice(choices.value[3].key, true)
|
|
787
|
+
break
|
|
788
|
+
case 'KeyE':
|
|
789
|
+
choices.value[4] && selectChoice(choices.value[4].key, true)
|
|
790
|
+
break
|
|
791
|
+
case 'KeyF':
|
|
792
|
+
choices.value[5] && selectChoice(choices.value[5].key, true)
|
|
793
|
+
break
|
|
794
|
+
case 'KeyG':
|
|
795
|
+
choices.value[6] && selectChoice(choices.value[6].key, true)
|
|
796
|
+
break
|
|
797
|
+
case 'KeyH':
|
|
798
|
+
choices.value[7] && selectChoice(choices.value[7].key, true)
|
|
799
|
+
break
|
|
800
|
+
case 'KeyI':
|
|
801
|
+
choices.value[8] && selectChoice(choices.value[8].key, true)
|
|
802
|
+
break
|
|
803
|
+
case 'KeyJ':
|
|
804
|
+
choices.value[9] && selectChoice(choices.value[9].key, true)
|
|
805
|
+
break
|
|
806
|
+
case 'KeyX':
|
|
807
|
+
showAnswers.value && toggleExplanation()
|
|
808
|
+
break
|
|
809
|
+
case 'Escape':
|
|
810
|
+
emitClose()
|
|
811
|
+
e.preventDefault()
|
|
812
|
+
break
|
|
813
|
+
case 'Enter':
|
|
814
|
+
if (!showAnswers.value && selectedChoices.value.length && focusChoiceKey.value === null) {
|
|
815
|
+
clickCheckAnswer()
|
|
957
816
|
e.preventDefault()
|
|
958
|
-
break
|
|
959
|
-
case 'Enter':
|
|
960
|
-
if (!this.showAnswers && this.selectedChoices.length && this.focusChoiceKey === null) {
|
|
961
|
-
this.clickCheckAnswer()
|
|
962
|
-
e.preventDefault()
|
|
963
|
-
}
|
|
964
|
-
break
|
|
965
|
-
case 'ArrowLeft':
|
|
966
|
-
this.emitPreviousQuestion()
|
|
967
|
-
e.preventDefault()
|
|
968
|
-
break
|
|
969
|
-
case 'ArrowRight':
|
|
970
|
-
this.emitNextQuestion()
|
|
971
|
-
e.preventDefault()
|
|
972
|
-
break
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
moveFocusToPassage () {
|
|
977
|
-
setTimeout(() => {
|
|
978
|
-
const passageAndImageComp =
|
|
979
|
-
this.$refs['uikit-question__passage-and-image'] as ComponentPublicInstance | undefined
|
|
980
|
-
const passageTitleEl =
|
|
981
|
-
// eslint-disable-next-line max-len
|
|
982
|
-
passageAndImageComp?.$refs['uikit-question-passage-and-image__passage-and-image-title'] as HTMLElement | undefined
|
|
983
|
-
if (passageTitleEl) {
|
|
984
|
-
passageTitleEl?.focus()
|
|
985
817
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
818
|
+
break
|
|
819
|
+
case 'ArrowLeft':
|
|
820
|
+
emitPreviousQuestion()
|
|
821
|
+
e.preventDefault()
|
|
822
|
+
break
|
|
823
|
+
case 'ArrowRight':
|
|
824
|
+
emitNextQuestion()
|
|
825
|
+
e.preventDefault()
|
|
826
|
+
break
|
|
994
827
|
}
|
|
828
|
+
}
|
|
995
829
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
const keyword = target.innerText
|
|
1001
|
-
const location = target.getAttribute('data-location')
|
|
1002
|
-
const clickLocation = { x: event.clientX, y: event.clientY }
|
|
1003
|
-
return {
|
|
1004
|
-
keyword,
|
|
1005
|
-
location,
|
|
1006
|
-
clickLocation,
|
|
1007
|
-
target,
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
830
|
+
const moveFocusToPassage = () => {
|
|
831
|
+
const passageTitleEl = passageAndImageRef?.value?.passageTitleEl
|
|
832
|
+
if (passageTitleEl) {
|
|
833
|
+
passageTitleEl?.focus()
|
|
1010
834
|
}
|
|
835
|
+
}
|
|
1011
836
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
this.showExplanation = this.defaultShowExplanation === null ? true : this.defaultShowExplanation
|
|
1016
|
-
this.selectedChoices = this.answerKeys
|
|
1017
|
-
} else {
|
|
1018
|
-
this.showMatrixAnswers = true
|
|
1019
|
-
this.showExplanation = this.defaultShowExplanation === null ? true : this.defaultShowExplanation
|
|
1020
|
-
this.selectedMatrixChoices = this.matrixAnswerKeys as TMatrixChoiceKey[]
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
837
|
+
const moveFocusToPrompt = async () => {
|
|
838
|
+
promptEl?.value?.focus()
|
|
839
|
+
}
|
|
1023
840
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
841
|
+
const keywordClick = (event: MouseEvent) => {
|
|
842
|
+
const target = event.target as HTMLElement
|
|
843
|
+
if (target.classList.contains('keyword-highlight')) {
|
|
844
|
+
const keyword = target.innerText.trim()
|
|
845
|
+
const location = target.getAttribute('data-location')
|
|
846
|
+
const clickLocation = { x: event.clientX, y: event.clientY }
|
|
847
|
+
emit('keyword-click', {
|
|
848
|
+
keyword,
|
|
849
|
+
location,
|
|
850
|
+
clickLocation,
|
|
851
|
+
target,
|
|
852
|
+
})
|
|
1030
853
|
}
|
|
854
|
+
}
|
|
1031
855
|
|
|
1032
|
-
|
|
1033
|
-
|
|
856
|
+
const startReviewMode = () => {
|
|
857
|
+
if (!isMatrixQuestion.value) {
|
|
858
|
+
showAnswers.value = true
|
|
859
|
+
showExplanation.value = props.defaultShowExplanation === null ? true : props.defaultShowExplanation
|
|
860
|
+
selectedChoices.value = answerKeys.value
|
|
861
|
+
} else {
|
|
862
|
+
showMatrixAnswers.value = true
|
|
863
|
+
showExplanation.value = props.defaultShowExplanation === null ? true : props.defaultShowExplanation
|
|
864
|
+
selectedMatrixChoices.value = matrixAnswerKeys.value as TMatrixChoiceKey[]
|
|
1034
865
|
}
|
|
866
|
+
}
|
|
1035
867
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
868
|
+
const stopReviewMode = () => {
|
|
869
|
+
showAnswers.value = false
|
|
870
|
+
showMatrixAnswers.value = false
|
|
871
|
+
showExplanation.value = false
|
|
872
|
+
selectedChoices.value = []
|
|
873
|
+
selectedMatrixChoices.value = []
|
|
874
|
+
}
|
|
1039
875
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
const hashChar = (char: string, num: number) => ((num << 5) - num) + char.charCodeAt(0)
|
|
876
|
+
const updateSelectedChoices = (updatedChoices: TChoiceKey[]) => {
|
|
877
|
+
selectedChoices.value = [ ...updatedChoices ]
|
|
878
|
+
}
|
|
1044
879
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
.reduce((acc: number, char: string) => hashChar(char, acc) & hashChar(char, acc), 0)
|
|
880
|
+
const updateSelectedMatrixChoices = (matrixChoices: TMatrixChoiceKey[]) => {
|
|
881
|
+
selectedMatrixChoices.value = [ ...matrixChoices ]
|
|
882
|
+
}
|
|
1049
883
|
|
|
1050
|
-
|
|
1051
|
-
|
|
884
|
+
// deterministic shuffling of choices so they don't change order everytime you reload the component
|
|
885
|
+
const shuffleChoices = (choicesToShuffle: TChoice[]): TChoice[] => {
|
|
886
|
+
const sortedChoices = choicesToShuffle.sort((a, b) => {
|
|
887
|
+
const hashChar = (char: string, num: number) => ((num << 5) - num) + char.charCodeAt(0)
|
|
1052
888
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
acc.push(sortedChoice)
|
|
1058
|
-
}
|
|
889
|
+
const aHash = a.text?.split('')
|
|
890
|
+
.reduce((acc: number, char: string) => hashChar(char, acc) & hashChar(char, acc), 0)
|
|
891
|
+
const bHash = b.text?.split('')
|
|
892
|
+
.reduce((acc: number, char: string) => hashChar(char, acc) & hashChar(char, acc), 0)
|
|
1059
893
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
: sortedChoices
|
|
1063
|
-
}
|
|
894
|
+
return (aHash || 0) - (bHash || 0)
|
|
895
|
+
})
|
|
1064
896
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const classesString = relatedTarget.getAttribute('class') || ''
|
|
1071
|
-
const classes = classesString.split(' ')
|
|
1072
|
-
if (classes.includes('uikit-question-choices-container__strikethrough')) {
|
|
1073
|
-
return // Don't set focusChoiceKey = null
|
|
897
|
+
return props.answerSeed
|
|
898
|
+
? props.answerSeed.reduce<TChoice[]>((acc, i) => {
|
|
899
|
+
const sortedChoice = sortedChoices[i]
|
|
900
|
+
if (sortedChoice) {
|
|
901
|
+
acc.push(sortedChoice)
|
|
1074
902
|
}
|
|
903
|
+
|
|
904
|
+
return acc
|
|
905
|
+
}, [])
|
|
906
|
+
: sortedChoices
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const choiceFocusOut = (event: FocusEvent) => {
|
|
910
|
+
const relatedTarget = event.relatedTarget
|
|
911
|
+
if (
|
|
912
|
+
relatedTarget instanceof Element
|
|
913
|
+
) {
|
|
914
|
+
const classesString = relatedTarget.getAttribute('class') || ''
|
|
915
|
+
const classes = classesString.split(' ')
|
|
916
|
+
if (classes.includes('uikit-question-choices-container__strikethrough')) {
|
|
917
|
+
return // Don't set focusChoiceKey = null
|
|
1075
918
|
}
|
|
1076
|
-
this.focusChoiceKey = null
|
|
1077
919
|
}
|
|
920
|
+
focusChoiceKey.value = null
|
|
921
|
+
}
|
|
1078
922
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
923
|
+
const choiceFocusIn = (choiceKey: TChoiceKey) => {
|
|
924
|
+
focusChoiceKey.value = choiceKey
|
|
925
|
+
}
|
|
1082
926
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
927
|
+
const choiceMouseOver = (choiceKey: TChoiceKey) => {
|
|
928
|
+
hoverChoiceKey.value = choiceKey
|
|
929
|
+
}
|
|
1086
930
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
931
|
+
const choiceMouseLeave = () => {
|
|
932
|
+
hoverChoiceKey.value = null
|
|
933
|
+
}
|
|
1090
934
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
935
|
+
const handleTouchStart = (event: TouchEvent) => {
|
|
936
|
+
swipeStart.value = {
|
|
937
|
+
x: event?.touches[0]?.clientX || null,
|
|
938
|
+
y: event?.touches[0]?.clientY || null,
|
|
1096
939
|
}
|
|
940
|
+
}
|
|
1097
941
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
}
|
|
942
|
+
const handleTouchMove = (event: TouchEvent) => {
|
|
943
|
+
if (event.cancelable) {
|
|
944
|
+
const changedY = event?.changedTouches[0]?.clientY || null
|
|
945
|
+
const changedX = event?.changedTouches[0]?.clientX || null
|
|
946
|
+
if (
|
|
947
|
+
changedY !== null
|
|
948
|
+
&& changedX !== null
|
|
949
|
+
&& swipeStart.value.y !== null
|
|
950
|
+
&& swipeStart.value.x !== null
|
|
951
|
+
&& Math.abs(changedX - swipeStart.value.x) > 26
|
|
952
|
+
) {
|
|
953
|
+
event.stopPropagation()
|
|
954
|
+
event.preventDefault()
|
|
1112
955
|
}
|
|
1113
956
|
}
|
|
957
|
+
}
|
|
1114
958
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
959
|
+
const handleTouchEnd = (option: {choiceKey: TChoiceKey; event: TouchEvent}) => {
|
|
960
|
+
const choiceKey = option.choiceKey
|
|
961
|
+
const event = option.event
|
|
962
|
+
const targetEl = event.target as Ref
|
|
963
|
+
const swipeEnd = {
|
|
964
|
+
x: event?.changedTouches[0]?.clientX || null,
|
|
965
|
+
y: event?.changedTouches[0]?.clientY || null,
|
|
966
|
+
}
|
|
967
|
+
if (
|
|
968
|
+
targetEl instanceof HTMLElement
|
|
969
|
+
&& swipeEnd.x !== null
|
|
970
|
+
&& swipeEnd.y !== null
|
|
971
|
+
&& swipeStart.value.x !== null
|
|
972
|
+
&& Math.abs(swipeEnd.x - swipeStart.value.x) > 80
|
|
973
|
+
) {
|
|
974
|
+
const choiceEls = choicesContainerRef?.value?.choiceEls
|
|
975
|
+
const parent = choiceEls?.find(choiceEl => choiceEl.contains(targetEl))
|
|
976
|
+
const finalElement = document.elementFromPoint(swipeEnd.x, swipeEnd.y)
|
|
977
|
+
if (parent?.contains(finalElement)) {
|
|
978
|
+
clickChoiceStrike(choiceKey)
|
|
979
|
+
}
|
|
980
|
+
} else if (!showAnswers.value) {
|
|
981
|
+
if (event.cancelable) {
|
|
982
|
+
event.preventDefault()
|
|
1122
983
|
}
|
|
1123
984
|
if (
|
|
1124
|
-
|
|
1125
|
-
&&
|
|
1126
|
-
&& swipeEnd.y
|
|
1127
|
-
&& this.swipeStart.x !== null
|
|
1128
|
-
&& Math.abs(swipeEnd.x - this.swipeStart.x) > 80
|
|
985
|
+
swipeEnd.y !== null
|
|
986
|
+
&& swipeStart.value.y !== null
|
|
987
|
+
&& Math.abs(swipeEnd.y - swipeStart.value.y) < 20
|
|
1129
988
|
) {
|
|
1130
|
-
|
|
1131
|
-
'uikit-question__choices-container'
|
|
1132
|
-
] as ComponentPublicInstance | undefined
|
|
1133
|
-
const choiceEls = choicesContainerComp?.$refs.choices as HTMLElement[] | undefined
|
|
1134
|
-
const parent = choiceEls?.find(choiceEl => choiceEl.contains(targetEl))
|
|
1135
|
-
const finalElement = document.elementFromPoint(swipeEnd.x, swipeEnd.y)
|
|
1136
|
-
if (parent?.contains(finalElement)) {
|
|
1137
|
-
this.clickChoiceStrike(choiceKey)
|
|
1138
|
-
}
|
|
1139
|
-
} else if (!this.showAnswers) {
|
|
1140
|
-
if (event.cancelable) {
|
|
1141
|
-
event.preventDefault()
|
|
1142
|
-
}
|
|
1143
|
-
if (
|
|
1144
|
-
swipeEnd.y !== null
|
|
1145
|
-
&& this.swipeStart.y !== null
|
|
1146
|
-
&& Math.abs(swipeEnd.y - this.swipeStart.y) < 20
|
|
1147
|
-
) {
|
|
1148
|
-
this.selectChoice(choiceKey)
|
|
1149
|
-
}
|
|
989
|
+
selectChoice(choiceKey)
|
|
1150
990
|
}
|
|
1151
|
-
|
|
1152
|
-
this.swipeStart = { x: null, y: null }
|
|
1153
991
|
}
|
|
1154
992
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
return
|
|
1158
|
-
}
|
|
993
|
+
swipeStart.value = { x: null, y: null }
|
|
994
|
+
}
|
|
1159
995
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
choiceStrikes.splice(indexOfChoiceStrike, 1)
|
|
1164
|
-
} else {
|
|
1165
|
-
choiceStrikes.push(choiceKey)
|
|
1166
|
-
// If striking a selected choice, unselect it
|
|
1167
|
-
const selectedChoices = this.selectedChoices
|
|
1168
|
-
const indexOfSelectedChoice = selectedChoices.indexOf(choiceKey)
|
|
1169
|
-
if (indexOfSelectedChoice !== -1) {
|
|
1170
|
-
selectedChoices.splice(indexOfSelectedChoice, 1)
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
996
|
+
const clickChoiceStrike = (choiceKey: TChoiceKey) => {
|
|
997
|
+
if (showAnswers.value) {
|
|
998
|
+
return
|
|
1173
999
|
}
|
|
1174
1000
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1001
|
+
const indexOfChoiceStrike = choiceStrikes.value.indexOf(choiceKey)
|
|
1002
|
+
if (indexOfChoiceStrike !== -1) {
|
|
1003
|
+
choiceStrikes.value.splice(indexOfChoiceStrike, 1)
|
|
1004
|
+
} else {
|
|
1005
|
+
choiceStrikes.value.push(choiceKey)
|
|
1006
|
+
// If striking a selected choice, unselect it
|
|
1007
|
+
const indexOfSelectedChoice = selectedChoices.value.indexOf(choiceKey)
|
|
1181
1008
|
if (indexOfSelectedChoice !== -1) {
|
|
1182
|
-
selectedChoices.splice(indexOfSelectedChoice, 1)
|
|
1009
|
+
selectedChoices.value.splice(indexOfSelectedChoice, 1)
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const selectChoice = (choiceKey: TChoiceKey, focusChoice?: boolean) => {
|
|
1015
|
+
if (showAnswers.value) {
|
|
1016
|
+
return
|
|
1017
|
+
}
|
|
1018
|
+
const indexOfSelectedChoice = selectedChoices.value.indexOf(choiceKey)
|
|
1019
|
+
if (indexOfSelectedChoice !== -1) {
|
|
1020
|
+
selectedChoices.value.splice(indexOfSelectedChoice, 1)
|
|
1021
|
+
} else {
|
|
1022
|
+
if (isMCR.value) {
|
|
1023
|
+
selectedChoices.value.push(choiceKey)
|
|
1183
1024
|
} else {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
this.selectedChoices = [ choiceKey ]
|
|
1189
|
-
if (!this.showCheckAnswer) {
|
|
1190
|
-
this.clickCheckAnswer()
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
// If selecting a striked choice, unstrike it
|
|
1194
|
-
const choiceStrikes = this.choiceStrikes
|
|
1195
|
-
const indexOfChoiceStrike = this.choiceStrikes.indexOf(choiceKey)
|
|
1196
|
-
if (indexOfChoiceStrike !== -1) {
|
|
1197
|
-
choiceStrikes.splice(indexOfChoiceStrike, 1)
|
|
1025
|
+
hoverChoiceKey.value = null
|
|
1026
|
+
selectedChoices.value = [ choiceKey ]
|
|
1027
|
+
if (!props.showCheckAnswer) {
|
|
1028
|
+
clickCheckAnswer()
|
|
1198
1029
|
}
|
|
1199
1030
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
const choiceEl = choiceContainerComp?.$refs[`choice-${choiceKey}`] as HTMLElement | HTMLElement[];
|
|
1205
|
-
('length' in choiceEl)
|
|
1206
|
-
? choiceEl[0]?.focus()
|
|
1207
|
-
: choiceEl.focus()
|
|
1031
|
+
// If selecting a striked choice, unstrike it
|
|
1032
|
+
const indexOfChoiceStrike = choiceStrikes.value.indexOf(choiceKey)
|
|
1033
|
+
if (indexOfChoiceStrike !== -1) {
|
|
1034
|
+
choiceStrikes.value.splice(indexOfChoiceStrike, 1)
|
|
1208
1035
|
}
|
|
1209
1036
|
}
|
|
1210
1037
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1038
|
+
if (focusChoice) {
|
|
1039
|
+
const choiceEls = choicesContainerRef?.value?.choiceEls
|
|
1040
|
+
if (choiceEls && 'length' in choiceEls) {
|
|
1041
|
+
choiceEls[0]?.focus()
|
|
1214
1042
|
}
|
|
1215
|
-
this.selectedMatrixChoices = matrixChoiceKeys
|
|
1216
1043
|
}
|
|
1044
|
+
}
|
|
1217
1045
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
if (this.showPassageImageLongAlt) {
|
|
1222
|
-
setTimeout(() => {
|
|
1223
|
-
const mobileLongAltComp =
|
|
1224
|
-
this.$refs['uikit-question__passage-and-image-dropdown'] as ComponentPublicInstance | undefined
|
|
1225
|
-
const mobileImgDropdownImgDescription =
|
|
1226
|
-
// eslint-disable-next-line max-len
|
|
1227
|
-
mobileLongAltComp?.$refs['uikit-question-passage-and-image-dropdown__img-dropdown-img-description'] as HTMLElement | undefined
|
|
1228
|
-
|
|
1229
|
-
const passageAndImageComp =
|
|
1230
|
-
this.$refs['uikit-question__passage-and-image'] as ComponentPublicInstance | undefined
|
|
1231
|
-
const longAlt =
|
|
1232
|
-
passageAndImageComp?.$refs['uikit-question-passage-and-image__passage-image-description'] as Ref
|
|
1233
|
-
|
|
1234
|
-
// Checking offsetParent tells us which element is visible
|
|
1235
|
-
if (mobileImgDropdownImgDescription?.offsetParent) {
|
|
1236
|
-
mobileImgDropdownImgDescription.focus()
|
|
1237
|
-
} else if (longAlt?.offsetParent) {
|
|
1238
|
-
longAlt.focus()
|
|
1239
|
-
}
|
|
1240
|
-
}, 0)
|
|
1241
|
-
}
|
|
1046
|
+
const selectMatrixChoice = (matrixChoiceKeys: TMatrixChoiceKey[]) => {
|
|
1047
|
+
if (showMatrixAnswers.value) {
|
|
1048
|
+
return
|
|
1242
1049
|
}
|
|
1050
|
+
selectedMatrixChoices.value = matrixChoiceKeys
|
|
1051
|
+
}
|
|
1243
1052
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
if (this.showExplanationImageLongAlt) {
|
|
1247
|
-
setTimeout(() => {
|
|
1248
|
-
// checks for mobile / tablet portrait uikit-question-summary
|
|
1249
|
-
const mcrLongAltComp = this.$refs['uikit-question__summary'] as ComponentPublicInstance | undefined
|
|
1250
|
-
const mcrLongAlt =
|
|
1251
|
-
// eslint-disable-next-line max-len
|
|
1252
|
-
mcrLongAltComp?.$refs['uikit-question-summary__summary-dropdown-explanation-img-description'] as HTMLElement | undefined
|
|
1253
|
-
|
|
1254
|
-
const choiceContainerComp =
|
|
1255
|
-
this.$refs['uikit-question__choices-container'] as ComponentPublicInstance | undefined
|
|
1256
|
-
if (choiceContainerComp) {
|
|
1257
|
-
const dropdownExplanationComp =
|
|
1258
|
-
// eslint-disable-next-line max-len
|
|
1259
|
-
choiceContainerComp.$refs['uikit-question-choices-container__dropdown-explanation'] as ComponentPublicInstance[]
|
|
1260
|
-
if (dropdownExplanationComp) {
|
|
1261
|
-
const dropdownEl = dropdownExplanationComp[0] as ComponentPublicInstance | undefined
|
|
1262
|
-
const mobileImgDropdownImgDescription =
|
|
1263
|
-
// eslint-disable-next-line max-len
|
|
1264
|
-
dropdownEl?.$refs['uikit-question-dropdown-explanation__dropdown-explanation-img-description'] as HTMLElement | undefined
|
|
1265
|
-
|
|
1266
|
-
if (mobileImgDropdownImgDescription?.offsetParent) {
|
|
1267
|
-
mobileImgDropdownImgDescription.focus()
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1053
|
+
const togglePassageImageLongAlt = () => {
|
|
1054
|
+
showPassageImageLongAlt.value = !showPassageImageLongAlt.value
|
|
1271
1055
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1056
|
+
if (showPassageImageLongAlt.value) {
|
|
1057
|
+
const mobileImgDropdownImgDescriptionEl = passageImageDropdownRef?.value?.mobileImgDropdownImgDescriptionEl
|
|
1058
|
+
const longAltEl = passageAndImageRef?.value?.longAltEl
|
|
1275
1059
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
}
|
|
1282
|
-
}, 0)
|
|
1060
|
+
// Checking offsetParent tells us which element is visible
|
|
1061
|
+
if (mobileImgDropdownImgDescriptionEl?.offsetParent) {
|
|
1062
|
+
mobileImgDropdownImgDescriptionEl.focus()
|
|
1063
|
+
} else if (longAltEl?.offsetParent) {
|
|
1064
|
+
longAltEl.focus()
|
|
1283
1065
|
}
|
|
1284
1066
|
}
|
|
1067
|
+
}
|
|
1285
1068
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
summaryMCRComp?.$refs['uikit-question-summary__summary-toggle-explanation-text'] as HTMLElement | undefined
|
|
1295
|
-
|
|
1296
|
-
const choiceContainerComp =
|
|
1297
|
-
this.$refs['uikit-question__choices-container'] as ComponentPublicInstance | undefined
|
|
1298
|
-
|
|
1299
|
-
const showExplanationRef = (
|
|
1300
|
-
choiceContainerComp?.$refs['uikit-question-choices-container__toggle-explanation-text']
|
|
1301
|
-
) as Element[] | Element | undefined
|
|
1302
|
-
|
|
1303
|
-
if (summaryMCRExplanation) {
|
|
1304
|
-
summaryMCRExplanation?.focus()
|
|
1305
|
-
} else if (showExplanationRef) {
|
|
1306
|
-
const showExplanationEl = (
|
|
1307
|
-
showExplanationRef instanceof Array
|
|
1308
|
-
? showExplanationRef[0]
|
|
1309
|
-
: showExplanationRef
|
|
1310
|
-
) as HTMLElement | undefined
|
|
1311
|
-
showExplanationEl?.focus()
|
|
1312
|
-
}
|
|
1313
|
-
}, 0)
|
|
1314
|
-
} else {
|
|
1315
|
-
setTimeout(() => {
|
|
1316
|
-
const explanationComp =
|
|
1317
|
-
this.$refs['uikit-question__explanation'] as ComponentPublicInstance | undefined
|
|
1318
|
-
const explanationTitle = explanationComp?.$refs['explanation'] as HTMLElement | undefined
|
|
1319
|
-
if (explanationTitle) {
|
|
1320
|
-
explanationTitle?.focus()
|
|
1321
|
-
}
|
|
1322
|
-
}, 0)
|
|
1069
|
+
const toggleExplanationImageLongAlt = () => {
|
|
1070
|
+
showExplanationImageLongAlt.value = !showExplanationImageLongAlt.value
|
|
1071
|
+
if (showExplanationImageLongAlt.value) {
|
|
1072
|
+
// checks for mobile / tablet portrait uikit-question-summary
|
|
1073
|
+
const mcrLongAltEl = questionSummaryRef?.value?.mcrLongAltEl
|
|
1074
|
+
const mobileImgDropdownImgDescriptionEl = choicesContainerRef?.value?.mobileImgDropdownImgDescriptionEl
|
|
1075
|
+
if (mobileImgDropdownImgDescriptionEl?.offsetParent) {
|
|
1076
|
+
mobileImgDropdownImgDescriptionEl.focus()
|
|
1323
1077
|
}
|
|
1324
|
-
|
|
1078
|
+
const longAltEl = questionExplanationRef?.value?.longAltEl
|
|
1325
1079
|
|
|
1326
|
-
|
|
1327
|
-
if (
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
selectedChoices: this.selectedChoices,
|
|
1332
|
-
questionSerial: this.question.serial,
|
|
1333
|
-
})
|
|
1334
|
-
setTimeout(() => {
|
|
1335
|
-
const summaryMCRComp =
|
|
1336
|
-
this.$refs['uikit-question__summary'] as ComponentPublicInstance | undefined
|
|
1337
|
-
const summaryMCRExplanation =
|
|
1338
|
-
// eslint-disable-next-line max-len
|
|
1339
|
-
summaryMCRComp?.$refs['uikit-question-summary__summary-toggle-explanation-text'] as HTMLElement | undefined
|
|
1340
|
-
|
|
1341
|
-
const choiceContainerComp =
|
|
1342
|
-
this.$refs['uikit-question__choices-container'] as ComponentPublicInstance | undefined
|
|
1343
|
-
|
|
1344
|
-
const showExplanationRef = (
|
|
1345
|
-
choiceContainerComp?.$refs['uikit-question-choices-container__toggle-explanation-text']
|
|
1346
|
-
) as Element[] | Element | undefined
|
|
1347
|
-
|
|
1348
|
-
if (summaryMCRExplanation) {
|
|
1349
|
-
summaryMCRExplanation?.focus()
|
|
1350
|
-
} else if (showExplanationRef) {
|
|
1351
|
-
const showExplanationEl = (
|
|
1352
|
-
showExplanationRef instanceof Array
|
|
1353
|
-
? showExplanationRef[0]
|
|
1354
|
-
: showExplanationRef
|
|
1355
|
-
) as HTMLElement | undefined
|
|
1356
|
-
showExplanationEl?.focus()
|
|
1357
|
-
}
|
|
1358
|
-
}, 500)
|
|
1080
|
+
// Checking offsetParent tells us which element is visible
|
|
1081
|
+
if (mcrLongAltEl?.offsetParent) {
|
|
1082
|
+
mcrLongAltEl.focus()
|
|
1083
|
+
} else if (longAltEl?.offsetParent) {
|
|
1084
|
+
longAltEl.focus()
|
|
1359
1085
|
}
|
|
1360
1086
|
}
|
|
1087
|
+
}
|
|
1361
1088
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1089
|
+
const toggleExplanation = () => {
|
|
1090
|
+
showExplanation.value = !showExplanation.value
|
|
1091
|
+
if (!showExplanation.value) {
|
|
1092
|
+
const summaryMCRExplanationEl = questionSummaryRef?.value?.summaryMCRExplanationEl
|
|
1093
|
+
const showExplanationEls = choicesContainerRef?.value?.showExplanationEls
|
|
1094
|
+
|
|
1095
|
+
if (summaryMCRExplanationEl) {
|
|
1096
|
+
summaryMCRExplanationEl?.focus()
|
|
1097
|
+
} else if (showExplanationEls) {
|
|
1098
|
+
const showExplanationEl = (
|
|
1099
|
+
showExplanationEls instanceof Array
|
|
1100
|
+
? showExplanationEls[0]
|
|
1101
|
+
: showExplanationEls
|
|
1102
|
+
) as HTMLElement | undefined
|
|
1103
|
+
showExplanationEl?.focus()
|
|
1104
|
+
}
|
|
1105
|
+
} else {
|
|
1106
|
+
const explanationTitleEl = questionExplanationRef?.value?.explanationTitleEl
|
|
1107
|
+
if (explanationTitleEl) {
|
|
1108
|
+
explanationTitleEl?.focus()
|
|
1382
1109
|
}
|
|
1383
1110
|
}
|
|
1111
|
+
}
|
|
1384
1112
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
const row = students.slice(i, i + rowLength).reduce((acc, student) => {
|
|
1406
|
-
if (!acc.id) {
|
|
1407
|
-
// Need a unique ID for the row
|
|
1408
|
-
acc.id = student.id
|
|
1409
|
-
}
|
|
1410
|
-
if (!acc.nameOne && !acc.isFlaggedByNameOne) {
|
|
1411
|
-
// Does our row have a nameOne filled in?
|
|
1412
|
-
acc.nameOne = student.name
|
|
1413
|
-
acc.isFlaggedByNameOne = student.isFlaggedByStudent
|
|
1414
|
-
} else if (!acc.nameTwo && !acc.isFlaggedByNameTwo) {
|
|
1415
|
-
// Does our row have a nameTwo filled in?
|
|
1416
|
-
acc.nameTwo = student.name
|
|
1417
|
-
acc.isFlaggedByNameTwo = student.isFlaggedByStudent
|
|
1418
|
-
} else if (!acc.nameThree && !acc.isFlaggedByNameThree) {
|
|
1419
|
-
// Does our row have a nameThree filled in?
|
|
1420
|
-
acc.nameThree = student.name
|
|
1421
|
-
acc.isFlaggedByNameThree = student.isFlaggedByStudent
|
|
1422
|
-
}
|
|
1423
|
-
return acc
|
|
1424
|
-
}, {} as TNamesRow)
|
|
1425
|
-
rows.push(row)
|
|
1426
|
-
}
|
|
1113
|
+
const clickCheckAnswer = () => {
|
|
1114
|
+
if (!props.hideAnswer) {
|
|
1115
|
+
showAnswers.value = true
|
|
1116
|
+
emitCheckAnswer({
|
|
1117
|
+
isCorrect: isCorrect.value,
|
|
1118
|
+
selectedChoices: selectedChoices.value,
|
|
1119
|
+
questionSerial: props.question.serial,
|
|
1120
|
+
})
|
|
1121
|
+
const summaryMCRExplanation = questionSummaryRef?.value?.summaryMCRExplanationEl
|
|
1122
|
+
const showExplanationEls = choicesContainerRef?.value?.showExplanationEls
|
|
1123
|
+
|
|
1124
|
+
if (summaryMCRExplanation) {
|
|
1125
|
+
summaryMCRExplanation?.focus()
|
|
1126
|
+
} else if (showExplanationEls) {
|
|
1127
|
+
const showExplanationEl = (
|
|
1128
|
+
showExplanationEls instanceof Array
|
|
1129
|
+
? showExplanationEls[0]
|
|
1130
|
+
: showExplanationEls
|
|
1131
|
+
) as HTMLElement | undefined
|
|
1132
|
+
showExplanationEl?.focus()
|
|
1427
1133
|
}
|
|
1428
|
-
return rows
|
|
1429
1134
|
}
|
|
1135
|
+
}
|
|
1430
1136
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1137
|
+
const clickCheckMatrixAnswer = () => {
|
|
1138
|
+
if (!props.hideAnswer) {
|
|
1139
|
+
showMatrixAnswers.value = true
|
|
1140
|
+
|
|
1141
|
+
emitCheckAnswer({
|
|
1142
|
+
isCorrect: isMatrixQuestionCorrect.value,
|
|
1143
|
+
selectedChoices: selectedMatrixChoices.value,
|
|
1144
|
+
questionSerial: props.question.serial,
|
|
1145
|
+
})
|
|
1146
|
+
|
|
1147
|
+
const summaryMatrixExplanationEl = questionSummaryRef?.value?.summaryMatrixExplanationEl
|
|
1148
|
+
if (summaryMatrixExplanationEl) {
|
|
1149
|
+
summaryMatrixExplanationEl?.focus()
|
|
1437
1150
|
}
|
|
1438
1151
|
}
|
|
1152
|
+
}
|
|
1439
1153
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1154
|
+
watch(reviewMode, () => {
|
|
1155
|
+
if (reviewMode.value) {
|
|
1156
|
+
startReviewMode()
|
|
1157
|
+
} else {
|
|
1158
|
+
stopReviewMode()
|
|
1443
1159
|
}
|
|
1160
|
+
})
|
|
1444
1161
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
}
|
|
1162
|
+
watch(() => props.previousChoices, (previousChoices: TChoiceKey[] | undefined | null) => {
|
|
1163
|
+
if (previousChoices) {
|
|
1164
|
+
updateSelectedChoices(previousChoices)
|
|
1165
|
+
}
|
|
1166
|
+
}, { deep: true })
|
|
1167
|
+
|
|
1168
|
+
watch(() => props.previousMatrixChoices, (previousMatrixChoices: TMatrixChoiceKey[] | undefined | null) => {
|
|
1169
|
+
if (previousMatrixChoices) {
|
|
1170
|
+
updateSelectedMatrixChoices(previousMatrixChoices)
|
|
1171
|
+
}
|
|
1172
|
+
}, { deep: true })
|
|
1173
|
+
|
|
1174
|
+
watch(selectedChoices, () => {
|
|
1175
|
+
emitSelectedChoices({
|
|
1176
|
+
isCorrect: selectedChoices.value.length === answerKeys.value.length
|
|
1177
|
+
&& !selectedChoices.value.join(' ').includes('d'),
|
|
1178
|
+
selectedChoices: selectedChoices.value,
|
|
1179
|
+
questionSerial: props.question.serial,
|
|
1180
|
+
} as Study.Cloud.IQuizAnswer)
|
|
1181
|
+
}, { deep: true })
|
|
1182
|
+
|
|
1183
|
+
watch(selectedMatrixChoices, () => {
|
|
1184
|
+
emitSelectedChoices({
|
|
1185
|
+
isCorrect: selectedMatrixChoices.value.length === matrixAnswerKeys.value.length
|
|
1186
|
+
&& !selectedMatrixChoices.value.join(' ').includes('d'),
|
|
1187
|
+
selectedChoices: selectedMatrixChoices.value,
|
|
1188
|
+
questionSerial: props.question.serial,
|
|
1189
|
+
} as Study.Cloud.IQuizAnswer)
|
|
1190
|
+
}, { deep: true })
|
|
1191
|
+
|
|
1192
|
+
watch(showExplanation, () => {
|
|
1193
|
+
emitUpdateShowExplanation()
|
|
1194
|
+
})
|
|
1449
1195
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
}
|
|
1196
|
+
const emitSelectedChoices = (selectedChoicesToEmit: Study.Cloud.IQuizAnswer) => {
|
|
1197
|
+
emit('selectedChoices', selectedChoicesToEmit)
|
|
1198
|
+
}
|
|
1454
1199
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
isCorrect: this.selectedChoices.length === this.answerKeys.length
|
|
1459
|
-
&& !this.selectedChoices.join(' ').includes('d'),
|
|
1460
|
-
selectedChoices: this.selectedChoices,
|
|
1461
|
-
questionSerial: this.question.serial,
|
|
1462
|
-
} as Study.Cloud.IQuizAnswer)
|
|
1463
|
-
}
|
|
1200
|
+
const emitClose = () => {
|
|
1201
|
+
emit('close')
|
|
1202
|
+
}
|
|
1464
1203
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
isCorrect: this.selectedMatrixChoices.length === this.matrixAnswerKeys.length
|
|
1469
|
-
&& !this.selectedMatrixChoices.join(' ').includes('d'),
|
|
1470
|
-
selectedChoices: this.selectedMatrixChoices,
|
|
1471
|
-
questionSerial: this.question.serial,
|
|
1472
|
-
} as Study.Cloud.IQuizAnswer)
|
|
1473
|
-
}
|
|
1204
|
+
const emitUpgrade = () => {
|
|
1205
|
+
emit('upgrade')
|
|
1206
|
+
}
|
|
1474
1207
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}
|
|
1208
|
+
const emitSubmitQuiz = () => {
|
|
1209
|
+
emit('submitQuiz')
|
|
1210
|
+
}
|
|
1479
1211
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
}
|
|
1212
|
+
const emitNextQuestion = () => {
|
|
1213
|
+
emit('nextQuestion')
|
|
1214
|
+
}
|
|
1484
1215
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
}
|
|
1216
|
+
const emitPreviousQuestion = () => {
|
|
1217
|
+
emit('previousQuestion')
|
|
1218
|
+
}
|
|
1489
1219
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
}
|
|
1220
|
+
const emitCheckAnswer = (answer: Study.Cloud.IQuizAnswer) => {
|
|
1221
|
+
emit('checkAnswer', answer)
|
|
1222
|
+
}
|
|
1494
1223
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1224
|
+
const emitUpdateShowExplanation = () => {
|
|
1225
|
+
emit('update:showExplanation', showExplanation.value)
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
if (props.reviewMode) {
|
|
1229
|
+
startReviewMode()
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
if (!isMatrixQuestion.value && props.previousChoices) {
|
|
1233
|
+
updateSelectedChoices(props.previousChoices)
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
if (isMatrixQuestion.value && props.previousMatrixChoices) {
|
|
1237
|
+
updateSelectedMatrixChoices(props.previousMatrixChoices)
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
onMounted(async () => {
|
|
1241
|
+
questionEl.value = props.containerEl || questionRef.value
|
|
1242
|
+
|
|
1243
|
+
if (props.initialShowAnswers) {
|
|
1244
|
+
showAnswers.value = props.initialShowAnswers
|
|
1245
|
+
showMatrixAnswers.value = props.initialShowAnswers
|
|
1498
1246
|
}
|
|
1499
1247
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
return true
|
|
1248
|
+
if (props.allowKeyboardShortcuts) {
|
|
1249
|
+
window.addEventListener('keydown', keydownListener)
|
|
1503
1250
|
}
|
|
1504
1251
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1252
|
+
const promptElements = promptEl?.value?.querySelectorAll('p')
|
|
1253
|
+
if (promptElements?.length) {
|
|
1254
|
+
promptElements.forEach(el => el.setAttribute('tabindex', '0'))
|
|
1508
1255
|
}
|
|
1509
1256
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1257
|
+
if (props.autoFocusPrompt) {
|
|
1258
|
+
moveFocusToPrompt()
|
|
1259
|
+
} else if (props.autoFocusPrompt === false) {
|
|
1260
|
+
const contextEl = questionContextRef?.value?.contextEl
|
|
1261
|
+
contextEl?.focus()
|
|
1513
1262
|
}
|
|
1514
|
-
}
|
|
1263
|
+
})
|
|
1264
|
+
|
|
1265
|
+
onBeforeUnmount(() => {
|
|
1266
|
+
window.removeEventListener('keydown', keydownListener)
|
|
1267
|
+
})
|
|
1268
|
+
|
|
1269
|
+
// Provide question context once, instead of passing as props to every child
|
|
1270
|
+
provide(InjectionKeys.questionKey, question)
|
|
1271
|
+
provide(InjectionKeys.choicesKey, choices)
|
|
1272
|
+
provide(InjectionKeys.questionElKey, questionEl)
|
|
1273
|
+
provide(InjectionKeys.breakpointsWithElKey, breakpointsWithEl)
|
|
1274
|
+
provide(InjectionKeys.quizLengthKey, quizLength)
|
|
1275
|
+
provide(InjectionKeys.quizModeKey, quizMode)
|
|
1276
|
+
provide(InjectionKeys.questionNumberKey, questionNumber)
|
|
1277
|
+
provide(InjectionKeys.isDarkModeKey, isDarkMode)
|
|
1278
|
+
provide(InjectionKeys.isCorrectKey, isCorrect)
|
|
1279
|
+
provide(InjectionKeys.contextIconTypeKey, contextIconType)
|
|
1280
|
+
provide(InjectionKeys.showAnswersKey, showAnswers)
|
|
1281
|
+
provide(InjectionKeys.showMatrixAnswersKey, showMatrixAnswers)
|
|
1282
|
+
provide(InjectionKeys.imageUrlPrefixKey, imageUrlPrefix)
|
|
1283
|
+
provide(InjectionKeys.passageImageUrlKey, passageImageUrl)
|
|
1284
|
+
provide(InjectionKeys.passageImageAltKey, passageImageAlt)
|
|
1285
|
+
provide(InjectionKeys.passageImageLongAltKey, passageImageLongAlt)
|
|
1286
|
+
provide(InjectionKeys.showPassageImageLongAltKey, showPassageImageLongAlt)
|
|
1287
|
+
provide(InjectionKeys.passageAndImageTitleKey, passageAndImageTitle)
|
|
1288
|
+
provide(InjectionKeys.showExplanationKey, showExplanation)
|
|
1289
|
+
provide(InjectionKeys.isMCRKey, isMCR)
|
|
1290
|
+
provide(InjectionKeys.isUnansweredKey, isUnanswered)
|
|
1291
|
+
provide(InjectionKeys.answerKeysKey, answerKeys)
|
|
1292
|
+
provide(InjectionKeys.hoverChoiceKeyKey, hoverChoiceKey)
|
|
1293
|
+
provide(InjectionKeys.focusChoiceKeyKey, focusChoiceKey)
|
|
1294
|
+
provide(InjectionKeys.selectedChoicesKey, selectedChoices)
|
|
1295
|
+
provide(InjectionKeys.distractorKeysKey, distractorKeys)
|
|
1296
|
+
provide(InjectionKeys.choiceStrikesKey, choiceStrikes)
|
|
1297
|
+
provide(InjectionKeys.choiceScoresKey, choiceScores)
|
|
1298
|
+
provide(InjectionKeys.globalMetricsKey, globalMetrics)
|
|
1299
|
+
provide(InjectionKeys.reviewModeKey, reviewMode)
|
|
1300
|
+
provide(InjectionKeys.showExplanationImageLongAltKey, showExplanationImageLongAlt)
|
|
1301
|
+
provide(InjectionKeys.explanationImageUrlKey, explanationImageUrl)
|
|
1302
|
+
provide(InjectionKeys.explanationImageAltKey, explanationImageAlt)
|
|
1303
|
+
provide(InjectionKeys.explanationImageLongAltKey, explanationImageLongAlt)
|
|
1304
|
+
provide(InjectionKeys.referenceKey, reference)
|
|
1305
|
+
provide(InjectionKeys.hideReferencesKey, hideReferences)
|
|
1306
|
+
provide(InjectionKeys.keywordDefinitionsKey, keywordDefinitions)
|
|
1307
|
+
provide(InjectionKeys.showPaywallKey, showPaywall)
|
|
1308
|
+
provide(InjectionKeys.showPassageAndImageKey, showPassageAndImage)
|
|
1309
|
+
provide(InjectionKeys.isMatrixQuestionKey, isMatrixQuestion)
|
|
1310
|
+
provide(InjectionKeys.matrixChoiceScoresKey, matrixChoiceScores)
|
|
1311
|
+
provide(InjectionKeys.isMatrixQuestionCorrectKey, isMatrixQuestionCorrect)
|
|
1312
|
+
provide(InjectionKeys.matrixAnswerKeysKey, matrixAnswerKeys)
|
|
1313
|
+
provide(InjectionKeys.selectedMatrixChoicesKey, selectedMatrixChoices)
|
|
1314
|
+
provide(InjectionKeys.isTeachGroupReviewKey, isTeachGroupReview)
|
|
1515
1315
|
</script>
|
|
1516
1316
|
|
|
1517
1317
|
<style lang="scss">
|