@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.
@@ -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="!isMatrixQuestion" class="uikit-question-stats-summary__stats-summary-value">
8
- {{ choiceScores.totalAnswered }}
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="!isMatrixQuestion" class="uikit-question-stats-summary__stats-summary-value">
19
- {{ Math.round((choiceScores.answeredCorrectly / choiceScores.totalAnswered) * 100) }}%
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
- &--tablet-portrait {
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 isTeachGroupReviewKey = Symbol('isTeachGroupReview') as InjectionKey<Ref<boolean>>
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': !(isMCR || isUnanswered || isMatrixQuestion)
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="question.type !== 'Matrix Checkbox' && question.type !== 'Matrix Radio Button'"
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="((isMCR && showAnswers) || (isMatrixQuestion && showMatrixAnswers)) && !showPaywall"
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="!showAnswers && !hideAnswer && (showCheckAnswer || isMCR) && !isMatrixQuestion"
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="(showAnswers || showMatrixAnswers || hideAnswer) && (questionNumber >= quizLength)"
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="(showAnswers || showMatrixAnswers || hideAnswer ) && showNextQuestion"
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 (!isMatrixQuestion.value) {
759
- return selectedChoices.value.length === 0
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 !isMatrixQuestionAnswered.value
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
- if (!isMatrixQuestion.value) {
858
- showAnswers.value = true
859
- showExplanation.value = props.defaultShowExplanation === null ? true : props.defaultShowExplanation
860
- selectedChoices.value = answerKeys.value
861
- } else {
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.5.30",
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.14.43",
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",