@pocketprep/ui-kit 3.5.30 → 3.6.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.css +1 -1
- package/dist/@pocketprep/ui-kit.js +10505 -10179
- package/dist/@pocketprep/ui-kit.js.map +1 -1
- package/dist/@pocketprep/ui-kit.umd.cjs +11 -11
- package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
- package/lib/components/Quiz/Question/BuildListChoicesContainer.vue +720 -0
- package/lib/components/Quiz/Question/StatsSummary.vue +14 -6
- package/lib/components/Quiz/Question/Summary.vue +11 -2
- package/lib/components/Quiz/Question/composables.ts +10 -1
- package/lib/components/Quiz/Question/injectionSymbols.ts +11 -1
- package/lib/components/Quiz/Question.vue +210 -16
- package/lib/components/Quiz/question.d.ts +7 -0
- package/package.json +2 -2
|
@@ -4,23 +4,29 @@
|
|
|
4
4
|
class="uikit-question-stats-summary"
|
|
5
5
|
>
|
|
6
6
|
<div v-dark="isDarkMode" class="uikit-question-stats-summary__stats-summary-total">
|
|
7
|
-
<div v-if="
|
|
8
|
-
{{
|
|
7
|
+
<div v-if="isBuildListQuestion" class="uikit-question-stats-summary__stats-summary-value">
|
|
8
|
+
{{ buildListChoiceScores.totalAnswered }}
|
|
9
9
|
</div>
|
|
10
|
-
<div v-else class="uikit-question-stats-summary__stats-summary-value">
|
|
10
|
+
<div v-else-if="isMatrixQuestion" class="uikit-question-stats-summary__stats-summary-value">
|
|
11
11
|
{{ matrixChoiceScores.totalAnswered }}
|
|
12
12
|
</div>
|
|
13
|
+
<div v-else class="uikit-question-stats-summary__stats-summary-value">
|
|
14
|
+
{{ choiceScores.totalAnswered }}
|
|
15
|
+
</div>
|
|
13
16
|
<div v-dark="isDarkMode" class="uikit-question-stats-summary__stats-summary-label">
|
|
14
17
|
Studiers Answered
|
|
15
18
|
</div>
|
|
16
19
|
</div>
|
|
17
20
|
<div v-dark="isDarkMode" class="uikit-question-stats-summary__stats-summary-correct">
|
|
18
|
-
<div v-if="
|
|
19
|
-
{{ Math.round((
|
|
21
|
+
<div v-if="isBuildListQuestion" class="uikit-question-stats-summary__stats-summary-value">
|
|
22
|
+
{{ Math.round((buildListChoiceScores.answeredCorrectly / buildListChoiceScores.totalAnswered) * 100) }}%
|
|
20
23
|
</div>
|
|
21
|
-
<div v-else class="uikit-question-stats-summary__stats-summary-value">
|
|
24
|
+
<div v-else-if="isMatrixQuestion" class="uikit-question-stats-summary__stats-summary-value">
|
|
22
25
|
{{ Math.round((matrixChoiceScores.answeredCorrectly / matrixChoiceScores.totalAnswered) * 100) }}%
|
|
23
26
|
</div>
|
|
27
|
+
<div v-else class="uikit-question-stats-summary__stats-summary-value">
|
|
28
|
+
{{ Math.round((choiceScores.answeredCorrectly / choiceScores.totalAnswered) * 100) }}%
|
|
29
|
+
</div>
|
|
24
30
|
<div v-dark="isDarkMode" class="uikit-question-stats-summary__stats-summary-label">
|
|
25
31
|
Answered Correctly
|
|
26
32
|
</div>
|
|
@@ -38,6 +44,8 @@ const {
|
|
|
38
44
|
isDarkMode,
|
|
39
45
|
isMatrixQuestion,
|
|
40
46
|
matrixChoiceScores,
|
|
47
|
+
buildListChoiceScores,
|
|
48
|
+
isBuildListQuestion,
|
|
41
49
|
} = useQuestionContext()
|
|
42
50
|
</script>
|
|
43
51
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
:class="{
|
|
8
8
|
'uikit-question-summary--matrix-question': isMatrixQuestion,
|
|
9
9
|
'uikit-question-summary--matrix-question-review-mode': isMatrixQuestion && reviewMode,
|
|
10
|
+
'uikit-question-summary--build-list-question': isBuildListQuestion,
|
|
10
11
|
}"
|
|
11
12
|
>
|
|
12
13
|
<div class="uikit-question-summary__summary-title">
|
|
@@ -148,17 +149,20 @@ const {
|
|
|
148
149
|
hideReferences,
|
|
149
150
|
isCorrect,
|
|
150
151
|
isMatrixQuestionCorrect,
|
|
152
|
+
isBuildListOrderCorrect,
|
|
151
153
|
isUnanswered,
|
|
152
154
|
reviewMode,
|
|
153
155
|
isDarkMode,
|
|
154
156
|
isMatrixQuestion,
|
|
157
|
+
isBuildListQuestion,
|
|
155
158
|
breakpointsWithEl,
|
|
156
159
|
keywordDefinitions,
|
|
157
160
|
} = useQuestionContext()
|
|
158
161
|
|
|
159
162
|
const isQuestionCorrect = computed(() => {
|
|
160
163
|
return (!isMatrixQuestion.value && isCorrect.value) ||
|
|
161
|
-
(isMatrixQuestion.value && isMatrixQuestionCorrect.value && !isUnanswered.value)
|
|
164
|
+
(isMatrixQuestion.value && isMatrixQuestionCorrect.value && !isUnanswered.value) ||
|
|
165
|
+
(isBuildListQuestion.value && isBuildListOrderCorrect.value)
|
|
162
166
|
})
|
|
163
167
|
|
|
164
168
|
const summary = computed(() => {
|
|
@@ -208,10 +212,15 @@ const toggleSummaryExplanationImageLongAlt = () => {
|
|
|
208
212
|
display: inline-block;
|
|
209
213
|
}
|
|
210
214
|
|
|
211
|
-
&--
|
|
215
|
+
&--build-list-question {
|
|
216
|
+
max-width: 452px;
|
|
217
|
+
margin-left: 38px;
|
|
212
218
|
display: inline-block;
|
|
213
219
|
}
|
|
214
220
|
|
|
221
|
+
&--tablet-portrait {
|
|
222
|
+
display: inline-block;
|
|
223
|
+
}
|
|
215
224
|
|
|
216
225
|
&__summary-title {
|
|
217
226
|
font-weight: 600;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inject, ref } from 'vue'
|
|
2
2
|
import * as InjectionKeys from './injectionSymbols'
|
|
3
|
-
import type { TChoice, TChoiceKey, TMatrixChoiceKey } from '../question'
|
|
3
|
+
import type { TChoice, TChoiceKey, TMatrixChoiceKey, TBuildListChoice } from '../question'
|
|
4
4
|
|
|
5
5
|
export const useQuestionContext = () => {
|
|
6
6
|
const question = inject(InjectionKeys.questionKey)
|
|
@@ -15,6 +15,7 @@ export const useQuestionContext = () => {
|
|
|
15
15
|
question,
|
|
16
16
|
choiceScores,
|
|
17
17
|
choices: inject(InjectionKeys.choicesKey, ref<TChoice[]>([])),
|
|
18
|
+
buildListChoices: inject(InjectionKeys.buildListChoicesKey, ref<TBuildListChoice[]>([])),
|
|
18
19
|
questionEl: inject(InjectionKeys.questionElKey, ref(null)),
|
|
19
20
|
breakpointsWithEl: inject(InjectionKeys.breakpointsWithElKey, ref({
|
|
20
21
|
breakpoints: {
|
|
@@ -32,6 +33,7 @@ export const useQuestionContext = () => {
|
|
|
32
33
|
contextIconType: inject(InjectionKeys.contextIconTypeKey),
|
|
33
34
|
showAnswers: inject(InjectionKeys.showAnswersKey, ref(false)),
|
|
34
35
|
showMatrixAnswers: inject(InjectionKeys.showMatrixAnswersKey, ref(false)),
|
|
36
|
+
showBuildListOrder: inject(InjectionKeys.showBuildListOrderKey, ref(false)),
|
|
35
37
|
imageUrlPrefix: inject(InjectionKeys.imageUrlPrefixKey, ref('')),
|
|
36
38
|
passageImageUrl: inject(InjectionKeys.passageImageUrlKey, ref<string | null>(null)),
|
|
37
39
|
passageImageAlt: inject(InjectionKeys.passageImageAltKey, ref<string | undefined>('')),
|
|
@@ -59,13 +61,20 @@ export const useQuestionContext = () => {
|
|
|
59
61
|
showPaywall: inject(InjectionKeys.showPaywallKey, ref(false)),
|
|
60
62
|
showPassageAndImage: inject(InjectionKeys.showPassageAndImageKey, ref(false)),
|
|
61
63
|
isMatrixQuestion: inject(InjectionKeys.isMatrixQuestionKey, ref(false)),
|
|
64
|
+
isBuildListQuestion: inject(InjectionKeys.isBuildListQuestionKey, ref(false)),
|
|
62
65
|
matrixChoiceScores: inject(InjectionKeys.matrixChoiceScoresKey, ref({
|
|
63
66
|
totalAnswered: 0,
|
|
64
67
|
answeredCorrectly: 0,
|
|
65
68
|
})),
|
|
69
|
+
buildListChoiceScores: inject(InjectionKeys.buildListChoiceScoresKey, ref({
|
|
70
|
+
totalAnswered: 0,
|
|
71
|
+
answeredCorrectly: 0,
|
|
72
|
+
})),
|
|
66
73
|
isMatrixQuestionCorrect: inject(InjectionKeys.isMatrixQuestionCorrectKey, ref(false)),
|
|
74
|
+
isBuildListOrderCorrect: inject(InjectionKeys.isBuildListOrderCorrectKey, ref(false)),
|
|
67
75
|
matrixAnswerKeys: inject(InjectionKeys.matrixAnswerKeysKey, ref<TMatrixChoiceKey[]>([])),
|
|
68
76
|
selectedMatrixChoices: inject(InjectionKeys.selectedMatrixChoicesKey, ref([])),
|
|
77
|
+
selectedBuildListChoiceOrder: inject(InjectionKeys.selectedBuildListChoiceOrderKey, ref([])),
|
|
69
78
|
isTeachGroupReview: inject(InjectionKeys.isTeachGroupReviewKey, ref(false)),
|
|
70
79
|
}
|
|
71
80
|
}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import type { Study } from '@pocketprep/types'
|
|
2
3
|
import type {
|
|
4
|
+
TBuildListChoice,
|
|
3
5
|
TChoice,
|
|
4
6
|
TChoiceKey,
|
|
5
7
|
TChoiceScores,
|
|
6
8
|
TContextIcon,
|
|
7
9
|
TMatrixChoiceKey,
|
|
10
|
+
TBuildListChoiceKey,
|
|
8
11
|
TMatrixChoiceScores,
|
|
12
|
+
TBuildListChoiceScores,
|
|
9
13
|
TQuizMode,
|
|
10
14
|
} from '../question'
|
|
11
15
|
import type { ComputedRef, InjectionKey, Ref } from 'vue'
|
|
12
16
|
|
|
13
17
|
export const questionKey = Symbol('question') as InjectionKey<ComputedRef<Study.Class.QuestionJSON>>
|
|
14
18
|
export const choicesKey = Symbol('choices') as InjectionKey<ComputedRef<TChoice[]>>
|
|
19
|
+
export const buildListChoicesKey = Symbol('choices') as InjectionKey<ComputedRef<TBuildListChoice[]>>
|
|
15
20
|
export const questionElKey = Symbol('questionEl') as InjectionKey<Ref<Element | null>>
|
|
16
21
|
export const breakpointsWithElKey = Symbol('breakpointsWithEl') as InjectionKey<Ref<{
|
|
17
22
|
breakpoints: {
|
|
@@ -29,6 +34,7 @@ export const isCorrectKey = Symbol('isCorrect') as InjectionKey<ComputedRef<bool
|
|
|
29
34
|
export const contextIconTypeKey = Symbol('contextIconType') as InjectionKey<ComputedRef<TContextIcon>>
|
|
30
35
|
export const showAnswersKey = Symbol('showAnswers') as InjectionKey<Ref<boolean>>
|
|
31
36
|
export const showMatrixAnswersKey = Symbol('showMatrixAnswers') as InjectionKey<Ref<boolean>>
|
|
37
|
+
export const showBuildListOrderKey = Symbol('showBuildListOrder') as InjectionKey<Ref<boolean>>
|
|
32
38
|
export const imageUrlPrefixKey = Symbol('imageUrlPrefix') as InjectionKey<Ref<string>>
|
|
33
39
|
export const passageImageUrlKey = Symbol('passageImageUrl') as InjectionKey<ComputedRef<string | null>>
|
|
34
40
|
export const passageImageAltKey = Symbol('passageImageAlt') as InjectionKey<ComputedRef<string | undefined>>
|
|
@@ -62,8 +68,12 @@ export const keywordDefinitionsKey = Symbol('keywordDefinitions') as InjectionKe
|
|
|
62
68
|
export const showPaywallKey = Symbol('showPaywall') as InjectionKey<Ref<boolean>>
|
|
63
69
|
export const showPassageAndImageKey = Symbol('showPassageAndImage') as InjectionKey<ComputedRef<boolean>>
|
|
64
70
|
export const isMatrixQuestionKey = Symbol('isMatrixQuestion') as InjectionKey<ComputedRef<boolean>>
|
|
71
|
+
export const isBuildListQuestionKey = Symbol('isBuildListQuestion') as InjectionKey<ComputedRef<boolean>>
|
|
65
72
|
export const matrixChoiceScoresKey = Symbol('matrixChoiceScores') as InjectionKey<ComputedRef<TMatrixChoiceScores>>
|
|
73
|
+
export const buildListChoiceScoresKey = Symbol('buildListChoiceScores') as InjectionKey<ComputedRef<TBuildListChoiceScores>>
|
|
66
74
|
export const isMatrixQuestionCorrectKey = Symbol('isMatrixQuestionCorrect') as InjectionKey<ComputedRef<boolean>>
|
|
75
|
+
export const isBuildListOrderCorrectKey = Symbol('isBuildListOrderCorrect') as InjectionKey<ComputedRef<boolean>>
|
|
67
76
|
export const matrixAnswerKeysKey = Symbol('matrixAnswerKeys') as InjectionKey<ComputedRef<TMatrixChoiceKey[]>>
|
|
68
77
|
export const selectedMatrixChoicesKey = Symbol('selectedMatrixChoices') as InjectionKey<Ref<TMatrixChoiceKey[]>>
|
|
69
|
-
export const
|
|
78
|
+
export const selectedBuildListChoiceOrderKey = Symbol('selectedBuildListChoiceOrder') as InjectionKey<Ref<TBuildListChoiceKey[]>>
|
|
79
|
+
export const isTeachGroupReviewKey = Symbol('isTeachGroupReview') as InjectionKey<Ref<boolean>>
|
|
@@ -119,7 +119,8 @@
|
|
|
119
119
|
'uikit-question__choices--matrix': isMatrixQuestion,
|
|
120
120
|
'uikit-question__choices--unanswered': showAnswers && !isMCR && isUnanswered && !isCorrect,
|
|
121
121
|
'uikit-question__choices--show-stats': globalMetrics,
|
|
122
|
-
'uikit-question__choices--no-actions':
|
|
122
|
+
'uikit-question__choices--no-actions':
|
|
123
|
+
!(isMCR || isUnanswered || isMatrixQuestion || isBuildListQuestion)
|
|
123
124
|
&& !globalMetrics
|
|
124
125
|
&& (
|
|
125
126
|
reviewMode
|
|
@@ -148,7 +149,10 @@
|
|
|
148
149
|
/>
|
|
149
150
|
</div>
|
|
150
151
|
<ChoicesContainer
|
|
151
|
-
v-if="
|
|
152
|
+
v-if="
|
|
153
|
+
question.type !== 'Matrix Checkbox'
|
|
154
|
+
&& question.type !== 'Matrix Radio Button'
|
|
155
|
+
&& question.type !== 'Build List'"
|
|
152
156
|
ref="uikit-question__choices-container"
|
|
153
157
|
@emitChoiceMouseOver="choiceMouseOver"
|
|
154
158
|
@emitChoiceMouseLeave="choiceMouseLeave"
|
|
@@ -243,9 +247,23 @@
|
|
|
243
247
|
<slot name="explanationBottomExperiment" />
|
|
244
248
|
</template>
|
|
245
249
|
</MobileMatrixChoicesContainer>
|
|
250
|
+
<BuildListChoicesContainer
|
|
251
|
+
v-if="question.type === 'Build List'"
|
|
252
|
+
class="uikit-question__build-list-choices-container"
|
|
253
|
+
v-breakpoint="breakpointsWithEl"
|
|
254
|
+
@reorderBuildList="handleBuildListReorder"
|
|
255
|
+
>
|
|
256
|
+
<template #explanationBottomExperiment>
|
|
257
|
+
<slot name="explanationBottomExperiment" />
|
|
258
|
+
</template>
|
|
259
|
+
</BuildListChoicesContainer>
|
|
246
260
|
</div>
|
|
247
261
|
<Summary
|
|
248
|
-
v-if="
|
|
262
|
+
v-if="
|
|
263
|
+
((isMCR && showAnswers)
|
|
264
|
+
|| (isMatrixQuestion && showMatrixAnswers))
|
|
265
|
+
|| (isBuildListQuestion && showBuildListOrder)
|
|
266
|
+
&& !showPaywall"
|
|
249
267
|
ref="uikit-question__summary"
|
|
250
268
|
class="uikit-question__summary"
|
|
251
269
|
@click="keywordClick"
|
|
@@ -294,7 +312,12 @@
|
|
|
294
312
|
>
|
|
295
313
|
<slot name="action">
|
|
296
314
|
<PocketButton
|
|
297
|
-
v-if="
|
|
315
|
+
v-if="
|
|
316
|
+
!showAnswers
|
|
317
|
+
&& !hideAnswer
|
|
318
|
+
&& (showCheckAnswer || isMCR)
|
|
319
|
+
&& !isMatrixQuestion
|
|
320
|
+
&& !isBuildListQuestion"
|
|
298
321
|
:disabled="!selectedChoices.length"
|
|
299
322
|
:is-dark-mode="isDarkMode"
|
|
300
323
|
@click="clickCheckAnswer"
|
|
@@ -310,14 +333,28 @@
|
|
|
310
333
|
Check Answer
|
|
311
334
|
</PocketButton>
|
|
312
335
|
<PocketButton
|
|
313
|
-
v-else-if="
|
|
336
|
+
v-else-if="isBuildListQuestion && !showBuildListOrder && !hideAnswer"
|
|
337
|
+
:is-dark-mode="isDarkMode"
|
|
338
|
+
:disabled="!selectedBuildListChoiceOrder.length"
|
|
339
|
+
@click="clickCheckBuildListOrder"
|
|
340
|
+
>
|
|
341
|
+
Check Answer
|
|
342
|
+
</PocketButton>
|
|
343
|
+
<PocketButton
|
|
344
|
+
v-else-if="
|
|
345
|
+
(showAnswers || showMatrixAnswers || showBuildListOrder || hideAnswer)
|
|
346
|
+
&& (questionNumber >= quizLength)
|
|
347
|
+
"
|
|
314
348
|
:is-dark-mode="isDarkMode"
|
|
315
349
|
@click="emitSubmitQuiz"
|
|
316
350
|
>
|
|
317
351
|
Submit Quiz
|
|
318
352
|
</PocketButton>
|
|
319
353
|
<PocketButton
|
|
320
|
-
v-else-if="
|
|
354
|
+
v-else-if="
|
|
355
|
+
(showAnswers || showMatrixAnswers || showBuildListOrder || hideAnswer )
|
|
356
|
+
&& showNextQuestion
|
|
357
|
+
"
|
|
321
358
|
:is-dark-mode="isDarkMode"
|
|
322
359
|
@click="emitNextQuestion"
|
|
323
360
|
>
|
|
@@ -382,6 +419,7 @@ import Explanation from '../Quiz/Question/Explanation.vue'
|
|
|
382
419
|
import PassageAndImage from '../Quiz/Question/PassageAndImage.vue'
|
|
383
420
|
import MatrixChoicesContainer from '../Quiz/Question/MatrixChoicesContainer.vue'
|
|
384
421
|
import MobileMatrixChoicesContainer from '../Quiz/Question/MobileMatrixChoicesContainer.vue'
|
|
422
|
+
import BuildListChoicesContainer from './Question/BuildListChoicesContainer.vue'
|
|
385
423
|
import type { Study } from '@pocketprep/types'
|
|
386
424
|
import { highlightKeywordsInText, studyModes } from '../../utils'
|
|
387
425
|
import type {
|
|
@@ -389,9 +427,12 @@ import type {
|
|
|
389
427
|
TQuizMode,
|
|
390
428
|
TChoiceKey,
|
|
391
429
|
TMatrixChoiceKey,
|
|
430
|
+
TBuildListChoiceKey,
|
|
392
431
|
TChoice,
|
|
432
|
+
TBuildListChoice,
|
|
393
433
|
TChoiceScores,
|
|
394
434
|
TMatrixChoiceScores,
|
|
435
|
+
TBuildListChoiceScores,
|
|
395
436
|
TViewNames,
|
|
396
437
|
IScenarioSerial,
|
|
397
438
|
} from './question'
|
|
@@ -411,6 +452,7 @@ const props = withDefaults(defineProps<{
|
|
|
411
452
|
reviewMode?: boolean
|
|
412
453
|
previousChoices?: TChoiceKey[] | null
|
|
413
454
|
previousMatrixChoices?: TMatrixChoiceKey[] | null
|
|
455
|
+
previousBuildListChoices?: TBuildListChoiceKey[] | null
|
|
414
456
|
globalMetrics?: Study.Class.GlobalQuestionMetricJSON | null
|
|
415
457
|
showNames?: TViewNames | null
|
|
416
458
|
allowKeyboardShortcuts?: boolean
|
|
@@ -435,6 +477,7 @@ const props = withDefaults(defineProps<{
|
|
|
435
477
|
reviewMode: false,
|
|
436
478
|
previousChoices: null,
|
|
437
479
|
previousMatrixChoices: null,
|
|
480
|
+
previousBuildListChoices: null,
|
|
438
481
|
globalMetrics: null,
|
|
439
482
|
showNames: null,
|
|
440
483
|
allowKeyboardShortcuts: true,
|
|
@@ -488,8 +531,10 @@ const focusChoiceKey = ref<TChoiceKey | null>(null)
|
|
|
488
531
|
const choiceStrikes = ref<TChoiceKey[]>([])
|
|
489
532
|
const selectedChoices = ref<TChoiceKey[]>([])
|
|
490
533
|
const selectedMatrixChoices = ref<TMatrixChoiceKey[]>([])
|
|
534
|
+
const selectedBuildListChoiceOrder = ref<TBuildListChoiceKey[]>([])
|
|
491
535
|
const showAnswers = ref(false)
|
|
492
536
|
const showMatrixAnswers = ref(false)
|
|
537
|
+
const showBuildListOrder = ref(false)
|
|
493
538
|
const showExplanation = ref(false)
|
|
494
539
|
const showPassageImageLongAlt = ref(false)
|
|
495
540
|
const showExplanationImageLongAlt = ref(false)
|
|
@@ -517,6 +562,7 @@ const questionSummaryRef = useTemplateRef<{
|
|
|
517
562
|
mcrLongAltEl?: HTMLElement
|
|
518
563
|
summaryMCRExplanationEl?: HTMLElement
|
|
519
564
|
summaryMatrixExplanationEl?: HTMLElement
|
|
565
|
+
summaryBuildListExplanationEl?: HTMLElement
|
|
520
566
|
}>('uikit-question__summary')
|
|
521
567
|
const questionExplanationRef = useTemplateRef<{
|
|
522
568
|
explanationTitleEl?: HTMLElement
|
|
@@ -561,6 +607,10 @@ const isMatrixQuestion = computed(() => {
|
|
|
561
607
|
return props.question.type === 'Matrix Checkbox' || props.question.type === 'Matrix Radio Button'
|
|
562
608
|
})
|
|
563
609
|
|
|
610
|
+
const isBuildListQuestion = computed(() => {
|
|
611
|
+
return props.question.type === 'Build List'
|
|
612
|
+
})
|
|
613
|
+
|
|
564
614
|
const questionScenario = computed(() => {
|
|
565
615
|
return props.question.questionScenario as Study.Class.QuestionScenarioJSON | undefined
|
|
566
616
|
})
|
|
@@ -648,6 +698,16 @@ const matrixAnswerKeys = computed<TMatrixChoiceKey[]>(() => {
|
|
|
648
698
|
return [] as TMatrixChoiceKey[]
|
|
649
699
|
})
|
|
650
700
|
|
|
701
|
+
const correctOrderBuildListAnswerKeys = computed((): TBuildListChoiceKey[] => {
|
|
702
|
+
const buildListChoices = props.question.choices.map(choice => choice.id) as TBuildListChoiceKey[]
|
|
703
|
+
return buildListChoices
|
|
704
|
+
.sort((a, b) => {
|
|
705
|
+
const aNum = Number(a.substring(1))
|
|
706
|
+
const bNum = Number(b.substring(1))
|
|
707
|
+
return aNum - bNum
|
|
708
|
+
})
|
|
709
|
+
})
|
|
710
|
+
|
|
651
711
|
const distractors = computed((): TChoice[] => {
|
|
652
712
|
return props.question.choices.filter(choice => !choice.isCorrect).map((choice, index) => ({
|
|
653
713
|
text: choice.text,
|
|
@@ -666,6 +726,32 @@ const choices = computed(() => {
|
|
|
666
726
|
])
|
|
667
727
|
})
|
|
668
728
|
|
|
729
|
+
const buildListChoices = computed(() => {
|
|
730
|
+
const blChoices = props.question.choices.map(choice => {
|
|
731
|
+
return {
|
|
732
|
+
text: choice.text,
|
|
733
|
+
key: choice.id as TBuildListChoiceKey,
|
|
734
|
+
}
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
const shuffledChoices = shuffleBuildListChoices([ ...blChoices ])
|
|
738
|
+
|
|
739
|
+
// Check if the shuffled array is in correct order
|
|
740
|
+
const isInCorrectOrder = shuffledChoices.every((choice, index) => {
|
|
741
|
+
const expectedNumber = index + 1
|
|
742
|
+
const actualNumber = Number(choice.key.substring(1))
|
|
743
|
+
return actualNumber === expectedNumber
|
|
744
|
+
})
|
|
745
|
+
|
|
746
|
+
// If it is in the correct order, then we need to switch the first and second element
|
|
747
|
+
if (isInCorrectOrder && shuffledChoices.length >= 2) {
|
|
748
|
+
const [ first, second, ...rest ] = shuffledChoices
|
|
749
|
+
return [ second, first, ...rest ] as TBuildListChoice[]
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return shuffledChoices
|
|
753
|
+
})
|
|
754
|
+
|
|
669
755
|
const isCorrect = computed(() => {
|
|
670
756
|
// In order to be correct, user must have selected all the answers and none of the distractors
|
|
671
757
|
return showAnswers.value
|
|
@@ -679,6 +765,14 @@ const isMatrixQuestionCorrect = computed(() => {
|
|
|
679
765
|
&& !selectedMatrixChoices.value.join(' ').includes('d')
|
|
680
766
|
})
|
|
681
767
|
|
|
768
|
+
const isBuildListOrderCorrect = computed(() => {
|
|
769
|
+
return showBuildListOrder.value
|
|
770
|
+
&& correctOrderBuildListAnswerKeys.value.length === selectedBuildListChoiceOrder.value.length
|
|
771
|
+
&& correctOrderBuildListAnswerKeys.value.every(
|
|
772
|
+
(item, index) => item === selectedBuildListChoiceOrder.value[index]
|
|
773
|
+
)
|
|
774
|
+
})
|
|
775
|
+
|
|
682
776
|
const choiceScores = computed((): TChoiceScores => {
|
|
683
777
|
const metrics = globalMetrics.value
|
|
684
778
|
const scores: TChoiceScores = {
|
|
@@ -733,6 +827,27 @@ const matrixChoiceScores = computed((): TMatrixChoiceScores => {
|
|
|
733
827
|
return scores
|
|
734
828
|
})
|
|
735
829
|
|
|
830
|
+
const buildListChoiceScores = computed((): TBuildListChoiceScores => {
|
|
831
|
+
const metrics = props.globalMetrics
|
|
832
|
+
|
|
833
|
+
const scores: TBuildListChoiceScores = {
|
|
834
|
+
totalAnswered: selectedBuildListChoiceOrder.value.length &&
|
|
835
|
+
showBuildListOrder && !props.reviewMode ? 1 : 0,
|
|
836
|
+
answeredCorrectly: isBuildListOrderCorrect.value && !props.reviewMode ? 1 : 0,
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (!metrics) {
|
|
840
|
+
return scores
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
scores.totalAnswered += (
|
|
844
|
+
(metrics.answeredCorrectlyCount || 0)
|
|
845
|
+
+ (metrics.answeredIncorrectlyCount || 0)
|
|
846
|
+
)
|
|
847
|
+
scores.answeredCorrectly += (metrics.answeredCorrectlyCount || 0)
|
|
848
|
+
return scores
|
|
849
|
+
})
|
|
850
|
+
|
|
736
851
|
const isMatrixQuestionAnswered = computed(() => {
|
|
737
852
|
// Matrix questions are answered if each row has a selected choice
|
|
738
853
|
if (isMatrixQuestion.value) {
|
|
@@ -755,11 +870,15 @@ const isMatrixQuestionAnswered = computed(() => {
|
|
|
755
870
|
})
|
|
756
871
|
|
|
757
872
|
const isUnanswered = computed(() => {
|
|
758
|
-
if (
|
|
759
|
-
return
|
|
873
|
+
if (isBuildListQuestion.value) {
|
|
874
|
+
return selectedBuildListChoiceOrder.value.length === 0
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if (isMatrixQuestion.value) {
|
|
878
|
+
return !isMatrixQuestionAnswered.value
|
|
760
879
|
}
|
|
761
880
|
|
|
762
|
-
return
|
|
881
|
+
return selectedChoices.value.length === 0
|
|
763
882
|
})
|
|
764
883
|
|
|
765
884
|
const prompt = computed(() => {
|
|
@@ -854,20 +973,24 @@ const keywordClick = (event: MouseEvent) => {
|
|
|
854
973
|
}
|
|
855
974
|
|
|
856
975
|
const startReviewMode = () => {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
976
|
+
showExplanation.value = props.defaultShowExplanation === null ? true : props.defaultShowExplanation
|
|
977
|
+
|
|
978
|
+
if (isBuildListQuestion.value) {
|
|
979
|
+
showBuildListOrder.value = true
|
|
980
|
+
selectedBuildListChoiceOrder.value = correctOrderBuildListAnswerKeys.value
|
|
981
|
+
} else if (isMatrixQuestion.value) {
|
|
862
982
|
showMatrixAnswers.value = true
|
|
863
|
-
showExplanation.value = props.defaultShowExplanation === null ? true : props.defaultShowExplanation
|
|
864
983
|
selectedMatrixChoices.value = matrixAnswerKeys.value
|
|
984
|
+
} else {
|
|
985
|
+
showAnswers.value = true
|
|
986
|
+
selectedChoices.value = answerKeys.value
|
|
865
987
|
}
|
|
866
988
|
}
|
|
867
989
|
|
|
868
990
|
const stopReviewMode = () => {
|
|
869
991
|
showAnswers.value = false
|
|
870
992
|
showMatrixAnswers.value = false
|
|
993
|
+
showBuildListOrder.value = false
|
|
871
994
|
showExplanation.value = false
|
|
872
995
|
selectedChoices.value = []
|
|
873
996
|
selectedMatrixChoices.value = []
|
|
@@ -881,8 +1004,12 @@ const updateSelectedMatrixChoices = (matrixChoices: TMatrixChoiceKey[]) => {
|
|
|
881
1004
|
selectedMatrixChoices.value = [ ...matrixChoices ]
|
|
882
1005
|
}
|
|
883
1006
|
|
|
1007
|
+
const updateSelectedBuildListChoiceOrder = (buildListChoiceOrder: TBuildListChoiceKey[]) => {
|
|
1008
|
+
selectedBuildListChoiceOrder.value = [ ...buildListChoiceOrder ]
|
|
1009
|
+
}
|
|
1010
|
+
|
|
884
1011
|
// deterministic shuffling of choices so they don't change order everytime you reload the component
|
|
885
|
-
const shuffleChoices = (choicesToShuffle: TChoice[]): TChoice[]
|
|
1012
|
+
const shuffleChoices = (choicesToShuffle: TChoice[]): TChoice[]=> {
|
|
886
1013
|
const sortedChoices = choicesToShuffle.sort((a, b) => {
|
|
887
1014
|
const hashChar = (char: string, num: number) => ((num << 5) - num) + char.charCodeAt(0)
|
|
888
1015
|
|
|
@@ -906,6 +1033,30 @@ const shuffleChoices = (choicesToShuffle: TChoice[]): TChoice[] => {
|
|
|
906
1033
|
: sortedChoices
|
|
907
1034
|
}
|
|
908
1035
|
|
|
1036
|
+
const shuffleBuildListChoices = (choicesToShuffle: TBuildListChoice[]): TBuildListChoice[]=> {
|
|
1037
|
+
const sortedChoices = choicesToShuffle.sort((a, b) => {
|
|
1038
|
+
const hashChar = (char: string, num: number) => ((num << 5) - num) + char.charCodeAt(0)
|
|
1039
|
+
|
|
1040
|
+
const aHash = a.text?.split('')
|
|
1041
|
+
.reduce((acc: number, char: string) => hashChar(char, acc) & hashChar(char, acc), 0)
|
|
1042
|
+
const bHash = b.text?.split('')
|
|
1043
|
+
.reduce((acc: number, char: string) => hashChar(char, acc) & hashChar(char, acc), 0)
|
|
1044
|
+
|
|
1045
|
+
return (aHash || 0) - (bHash || 0)
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
return props.answerSeed
|
|
1049
|
+
? props.answerSeed.reduce<TBuildListChoice[]>((acc, i) => {
|
|
1050
|
+
const sortedChoice = sortedChoices[i]
|
|
1051
|
+
if (sortedChoice) {
|
|
1052
|
+
acc.push(sortedChoice)
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
return acc
|
|
1056
|
+
}, [])
|
|
1057
|
+
: sortedChoices
|
|
1058
|
+
}
|
|
1059
|
+
|
|
909
1060
|
const choiceFocusOut = (event: FocusEvent) => {
|
|
910
1061
|
const relatedTarget = event.relatedTarget
|
|
911
1062
|
if (
|
|
@@ -1050,6 +1201,14 @@ const selectMatrixChoice = (matrixChoiceKeys: TMatrixChoiceKey[]) => {
|
|
|
1050
1201
|
selectedMatrixChoices.value = matrixChoiceKeys
|
|
1051
1202
|
}
|
|
1052
1203
|
|
|
1204
|
+
const handleBuildListReorder = (reorderedChoices: TBuildListChoice[]) => {
|
|
1205
|
+
if (showBuildListOrder.value) {
|
|
1206
|
+
return
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
selectedBuildListChoiceOrder.value = reorderedChoices.map(choice => choice.key)
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1053
1212
|
const togglePassageImageLongAlt = () => {
|
|
1054
1213
|
showPassageImageLongAlt.value = !showPassageImageLongAlt.value
|
|
1055
1214
|
|
|
@@ -1151,6 +1310,24 @@ const clickCheckMatrixAnswer = () => {
|
|
|
1151
1310
|
}
|
|
1152
1311
|
}
|
|
1153
1312
|
|
|
1313
|
+
const clickCheckBuildListOrder = () => {
|
|
1314
|
+
if (!props.hideAnswer) {
|
|
1315
|
+
showBuildListOrder.value = true
|
|
1316
|
+
|
|
1317
|
+
emitCheckAnswer({
|
|
1318
|
+
isCorrect: isBuildListOrderCorrect.value,
|
|
1319
|
+
selectedChoices: selectedBuildListChoiceOrder.value,
|
|
1320
|
+
questionSerial: props.question.serial,
|
|
1321
|
+
})
|
|
1322
|
+
|
|
1323
|
+
const summaryBuildListExplanationEl = questionSummaryRef?.value?.summaryBuildListExplanationEl
|
|
1324
|
+
|
|
1325
|
+
if (summaryBuildListExplanationEl) {
|
|
1326
|
+
summaryBuildListExplanationEl?.focus()
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1154
1331
|
watch(reviewMode, () => {
|
|
1155
1332
|
if (reviewMode.value) {
|
|
1156
1333
|
startReviewMode()
|
|
@@ -1171,6 +1348,12 @@ watch(() => props.previousMatrixChoices, (previousMatrixChoices: TMatrixChoiceKe
|
|
|
1171
1348
|
}
|
|
1172
1349
|
}, { deep: true })
|
|
1173
1350
|
|
|
1351
|
+
watch(() => props.previousBuildListChoices, (previousBuildListChoices: TBuildListChoiceKey[] | undefined | null) => {
|
|
1352
|
+
if (previousBuildListChoices) {
|
|
1353
|
+
updateSelectedBuildListChoiceOrder(previousBuildListChoices)
|
|
1354
|
+
}
|
|
1355
|
+
}, { deep: true })
|
|
1356
|
+
|
|
1174
1357
|
watch(selectedChoices, () => {
|
|
1175
1358
|
emitSelectedChoices({
|
|
1176
1359
|
isCorrect: selectedChoices.value.length === answerKeys.value.length
|
|
@@ -1229,6 +1412,10 @@ if (props.reviewMode) {
|
|
|
1229
1412
|
startReviewMode()
|
|
1230
1413
|
}
|
|
1231
1414
|
|
|
1415
|
+
if (isBuildListQuestion.value && props.previousBuildListChoices) {
|
|
1416
|
+
updateSelectedBuildListChoiceOrder(props.previousBuildListChoices)
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1232
1419
|
if (!isMatrixQuestion.value && props.previousChoices) {
|
|
1233
1420
|
updateSelectedChoices(props.previousChoices)
|
|
1234
1421
|
}
|
|
@@ -1243,6 +1430,7 @@ onMounted(() => {
|
|
|
1243
1430
|
if (props.initialShowAnswers) {
|
|
1244
1431
|
showAnswers.value = props.initialShowAnswers
|
|
1245
1432
|
showMatrixAnswers.value = props.initialShowAnswers
|
|
1433
|
+
showBuildListOrder.value = props.initialShowAnswers
|
|
1246
1434
|
}
|
|
1247
1435
|
|
|
1248
1436
|
if (props.allowKeyboardShortcuts) {
|
|
@@ -1269,6 +1457,7 @@ onBeforeUnmount(() => {
|
|
|
1269
1457
|
// Provide question context once, instead of passing as props to every child
|
|
1270
1458
|
provide(InjectionKeys.questionKey, question)
|
|
1271
1459
|
provide(InjectionKeys.choicesKey, choices)
|
|
1460
|
+
provide(InjectionKeys.buildListChoicesKey, buildListChoices)
|
|
1272
1461
|
provide(InjectionKeys.questionElKey, questionEl)
|
|
1273
1462
|
provide(InjectionKeys.breakpointsWithElKey, breakpointsWithEl)
|
|
1274
1463
|
provide(InjectionKeys.quizLengthKey, quizLength)
|
|
@@ -1279,6 +1468,7 @@ provide(InjectionKeys.isCorrectKey, isCorrect)
|
|
|
1279
1468
|
provide(InjectionKeys.contextIconTypeKey, contextIconType)
|
|
1280
1469
|
provide(InjectionKeys.showAnswersKey, showAnswers)
|
|
1281
1470
|
provide(InjectionKeys.showMatrixAnswersKey, showMatrixAnswers)
|
|
1471
|
+
provide(InjectionKeys.showBuildListOrderKey, showBuildListOrder)
|
|
1282
1472
|
provide(InjectionKeys.imageUrlPrefixKey, imageUrlPrefix)
|
|
1283
1473
|
provide(InjectionKeys.passageImageUrlKey, passageImageUrl)
|
|
1284
1474
|
provide(InjectionKeys.passageImageAltKey, passageImageAlt)
|
|
@@ -1307,10 +1497,14 @@ provide(InjectionKeys.keywordDefinitionsKey, keywordDefinitions)
|
|
|
1307
1497
|
provide(InjectionKeys.showPaywallKey, showPaywall)
|
|
1308
1498
|
provide(InjectionKeys.showPassageAndImageKey, showPassageAndImage)
|
|
1309
1499
|
provide(InjectionKeys.isMatrixQuestionKey, isMatrixQuestion)
|
|
1500
|
+
provide(InjectionKeys.isBuildListQuestionKey, isBuildListQuestion)
|
|
1310
1501
|
provide(InjectionKeys.matrixChoiceScoresKey, matrixChoiceScores)
|
|
1502
|
+
provide(InjectionKeys.buildListChoiceScoresKey, buildListChoiceScores)
|
|
1311
1503
|
provide(InjectionKeys.isMatrixQuestionCorrectKey, isMatrixQuestionCorrect)
|
|
1504
|
+
provide(InjectionKeys.isBuildListOrderCorrectKey, isBuildListOrderCorrect)
|
|
1312
1505
|
provide(InjectionKeys.matrixAnswerKeysKey, matrixAnswerKeys)
|
|
1313
1506
|
provide(InjectionKeys.selectedMatrixChoicesKey, selectedMatrixChoices)
|
|
1507
|
+
provide(InjectionKeys.selectedBuildListChoiceOrderKey, selectedBuildListChoiceOrder)
|
|
1314
1508
|
provide(InjectionKeys.isTeachGroupReviewKey, isTeachGroupReview)
|
|
1315
1509
|
</script>
|
|
1316
1510
|
|
|
@@ -2,6 +2,7 @@ export type Ref = HTMLElement | null
|
|
|
2
2
|
|
|
3
3
|
export type TChoiceKey = `${'a' | 'd'}${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`
|
|
4
4
|
export type TMatrixChoiceKey = `${'a' | 'd'}${1 | 2 | 3 }_${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12}`
|
|
5
|
+
export type TBuildListChoiceKey = `a${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`
|
|
5
6
|
|
|
6
7
|
export type TQuizMode = 'qotd' | 'quick10' | 'timed' | 'weakest' | 'missed' | 'custom'
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ export type TBreakPointsObject = {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export type TChoice = { text?: string; key: TChoiceKey }
|
|
19
|
+
export type TBuildListChoice = { text?: string; key: TBuildListChoiceKey }
|
|
18
20
|
|
|
19
21
|
export type TChoiceScores = Partial<Record<TChoiceKey, number>> & {
|
|
20
22
|
totalAnswered: number
|
|
@@ -26,6 +28,11 @@ export type TMatrixChoiceScores = {
|
|
|
26
28
|
answeredCorrectly: number
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
export type TBuildListChoiceScores = {
|
|
32
|
+
totalAnswered: number
|
|
33
|
+
answeredCorrectly: number
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
export type TQuestionPassageAndImageTitle = 'Passage + Image' | 'Image' | 'Passage'
|
|
30
37
|
|
|
31
38
|
export type TNamesRow = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pocketprep/ui-kit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Pocket Prep UI Kit",
|
|
5
5
|
"author": "pocketprep",
|
|
6
6
|
"scripts": {
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"vue-router": "4.5.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@pocketprep/types": "1.
|
|
85
|
+
"@pocketprep/types": "1.15.13",
|
|
86
86
|
"@stylistic/eslint-plugin-ts": "4.2.0",
|
|
87
87
|
"@tsconfig/node22": "22.0.0",
|
|
88
88
|
"@types/node": "22.13.10",
|