@pocketprep/ui-kit 3.4.90 → 3.5.1

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.
Files changed (88) hide show
  1. package/README.md +2 -2
  2. package/dist/@pocketprep/ui-kit.css +1 -0
  3. package/dist/@pocketprep/ui-kit.js +16490 -18228
  4. package/dist/@pocketprep/ui-kit.js.map +1 -1
  5. package/dist/@pocketprep/ui-kit.umd.cjs +11 -11
  6. package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
  7. package/eslint.config.ts +111 -0
  8. package/lib/components/Banners/Banner.vue +2 -2
  9. package/lib/components/Bundles/BundleList.vue +1 -1
  10. package/lib/components/Bundles/BundleSearch.vue +43 -12
  11. package/lib/components/Bundles/PremiumPill.vue +2 -2
  12. package/lib/components/Buttons/Button.vue +19 -18
  13. package/lib/components/Buttons/Link.vue +9 -8
  14. package/lib/components/Buttons/Tab.vue +4 -3
  15. package/lib/components/Calendar/Calendar.vue +14 -2
  16. package/lib/components/Charts/Bar.vue +3 -3
  17. package/lib/components/Charts/Pie.vue +4 -4
  18. package/lib/components/Controls/SegmentControl.vue +8 -7
  19. package/lib/components/Controls/Slider.vue +2 -3
  20. package/lib/components/Controls/ToggleSwitch.vue +3 -2
  21. package/lib/components/EmptyStates/EmptyState.vue +3 -2
  22. package/lib/components/Exams/ExamCard.vue +3 -3
  23. package/lib/components/Exams/ExamMenuCard.vue +2 -2
  24. package/lib/components/Filters/FilterDropdown.vue +2 -2
  25. package/lib/components/Filters/FilterOptions.vue +2 -2
  26. package/lib/components/Forms/Checkbox.vue +2 -2
  27. package/lib/components/Forms/CheckboxOption.vue +2 -2
  28. package/lib/components/Forms/Errors.vue +2 -2
  29. package/lib/components/Forms/Input.vue +2 -2
  30. package/lib/components/Forms/Radio.vue +37 -39
  31. package/lib/components/Forms/RadioButton.vue +1 -1
  32. package/lib/components/Forms/Select.vue +7 -6
  33. package/lib/components/Forms/Textarea.vue +2 -2
  34. package/lib/components/Icons/Icon.vue +1 -0
  35. package/lib/components/Icons/IconEdit.vue +4 -2
  36. package/lib/components/Icons/IconFullViewActive.vue +1 -1
  37. package/lib/components/Icons/IconLoading2.vue +1 -3
  38. package/lib/components/Loaders/SkeletonLoader.vue +2 -2
  39. package/lib/components/Messaging/InfoMessage.vue +2 -2
  40. package/lib/components/Modal/Modal.vue +2 -2
  41. package/lib/components/Modal/ModalContainer.vue +2 -2
  42. package/lib/components/Onboarding/EmailAuth.vue +5 -5
  43. package/lib/components/Onboarding/MagicCodeEntry.vue +3 -4
  44. package/lib/components/Pagination/QuestionReviewPagination.vue +23 -21
  45. package/lib/components/Pagination/TablePagination.vue +2 -2
  46. package/lib/components/Quiz/FlagToggle.vue +2 -2
  47. package/lib/components/Quiz/GlobalMetricsToggle.vue +3 -2
  48. package/lib/components/Quiz/KeyboardShortcutsButton.vue +1 -1
  49. package/lib/components/Quiz/KeyboardShortcutsModal.vue +1 -1
  50. package/lib/components/Quiz/Question/ChoicesContainer.vue +99 -132
  51. package/lib/components/Quiz/Question/DropdownExplanation.vue +41 -55
  52. package/lib/components/Quiz/Question/Explanation.vue +49 -59
  53. package/lib/components/Quiz/Question/MatrixChoicesContainer.vue +208 -226
  54. package/lib/components/Quiz/Question/MatrixRadioGroup.vue +7 -6
  55. package/lib/components/Quiz/Question/MobileMatrixChoicesContainer.vue +315 -320
  56. package/lib/components/Quiz/Question/MobileMatrixRadioGroup.vue +14 -11
  57. package/lib/components/Quiz/Question/PassageAndImage.vue +34 -45
  58. package/lib/components/Quiz/Question/PassageAndImageDropdown.vue +39 -49
  59. package/lib/components/Quiz/Question/Paywall.vue +30 -41
  60. package/lib/components/Quiz/Question/QuestionContext.vue +24 -33
  61. package/lib/components/Quiz/Question/StatsSummary.vue +12 -22
  62. package/lib/components/Quiz/Question/Summary.vue +56 -66
  63. package/lib/components/Quiz/Question/composables.ts +71 -0
  64. package/lib/components/Quiz/Question/injectionSymbols.ts +69 -0
  65. package/lib/components/Quiz/Question.vue +810 -1009
  66. package/lib/components/Quiz/QuizContainer.vue +63 -67
  67. package/lib/components/Quiz/QuizProgress.vue +73 -77
  68. package/lib/components/Quiz/QuizProgressBar.vue +3 -2
  69. package/lib/components/Quiz/question.d.ts +4 -4
  70. package/lib/components/Search/Pill.vue +2 -2
  71. package/lib/components/Search/Search.vue +2 -2
  72. package/lib/components/SidePanels/SidePanel.vue +8 -3
  73. package/lib/components/Tables/Table.vue +4 -3
  74. package/lib/components/Tables/TableActions.vue +3 -3
  75. package/lib/components/Tags/Tag.vue +2 -2
  76. package/lib/components/Toasts/Toast.vue +5 -3
  77. package/lib/components/Tooltips/OverflowTooltip.vue +2 -2
  78. package/lib/components/Tooltips/Tooltip.vue +2 -2
  79. package/lib/directives.ts +28 -23
  80. package/lib/pocketprep-export.module.scss +3 -2
  81. package/lib/pocketprep.scss +2 -2
  82. package/lib/styles/_breakpoints.scss +12 -24
  83. package/lib/styles/_colors.scss +0 -1
  84. package/package.json +38 -29
  85. package/stylelint.config.js +38 -0
  86. package/.eslintrc.cjs +0 -74
  87. package/dist/style.css +0 -1
  88. package/stylelint.config.cjs +0 -22
@@ -6,7 +6,7 @@
6
6
  ref="uikit-question-choices-container"
7
7
  >
8
8
  <div
9
- ref="choices"
9
+ ref="choicesRef"
10
10
  v-dark="isDarkMode"
11
11
  class="uikit-question-choices-container__choice-container"
12
12
  :class="{
@@ -40,7 +40,7 @@
40
40
  >
41
41
  <div
42
42
  v-if="!reviewMode"
43
- v-breakpoint:questionEl="breakpoints"
43
+ v-breakpoint="breakpointsWithEl"
44
44
  v-dark="isDarkMode"
45
45
  class="uikit-question-choices-container__choice-letter"
46
46
  :class="!isMCR && {
@@ -60,7 +60,7 @@
60
60
  <div
61
61
  :ref="`choice-${choice.key}`"
62
62
  v-dark="isDarkMode"
63
- v-breakpoint:questionEl="breakpoints"
63
+ v-breakpoint="breakpointsWithEl"
64
64
  class="uikit-question-choices-container__choice"
65
65
  :class="{
66
66
  'uikit-question-choices-container__choice--strike': showAnswers
@@ -111,7 +111,7 @@
111
111
  'uikit-question-choices-container__choice-text--review-mode':
112
112
  reviewMode,
113
113
  }"
114
- v-breakpoint:questionEl="breakpoints"
114
+ v-breakpoint="breakpointsWithEl"
115
115
  :tabindex="showAnswers ? -1 : 0"
116
116
  :role="isMCR
117
117
  ? 'checkbox'
@@ -129,7 +129,7 @@
129
129
  />
130
130
  <PocketButton
131
131
  v-if="!isMCR && showAnswers && answerKeys.includes(choice.key)"
132
- v-breakpoint:questionEl="breakpoints"
132
+ v-breakpoint="breakpointsWithEl"
133
133
  type="tertiary-small"
134
134
  class="uikit-question-choices-container__toggle-explanation"
135
135
  :class="{ 'uikit-question-choices-container__toggle-explanation--review-mode': reviewMode }"
@@ -144,7 +144,7 @@
144
144
  tabindex="-1"
145
145
  >{{ showExplanation ? 'Hide' : 'Show' }} Explanation</span>
146
146
  <Icon
147
- v-breakpoint:questionEl="breakpoints"
147
+ v-breakpoint="breakpointsWithEl"
148
148
  class="uikit-question-choices-container__toggle-explanation-icon"
149
149
  :class="{
150
150
  'uikit-question-choices-container__toggle-explanation-icon--up': showExplanation,
@@ -156,22 +156,6 @@
156
156
  v-if="!isMCR && showAnswers && answerKeys.includes(choice.key) && showExplanation"
157
157
  ref="uikit-question-choices-container__dropdown-explanation"
158
158
  class="uikit-question-choices-container__dropdown-explanation"
159
- :question="question"
160
- :answer-keys="answerKeys"
161
- :choice="choice"
162
- :global-metrics="globalMetrics"
163
- :show-answers="showAnswers"
164
- :is-MCR="isMCR"
165
- :show-explanation-image-long-alt="showExplanationImageLongAlt"
166
- :explanation-image-url="explanationImageUrl"
167
- :explanation-image-alt="explanationImageAlt"
168
- :explanation-image-long-alt="explanationImageLongAlt"
169
- :reference="reference"
170
- :hide-references="hideReferences"
171
- :is-dark-mode="isDarkMode"
172
- :question-el="questionEl"
173
- :breakpoints="breakpoints"
174
- :keyword-definitions="keywordDefinitions"
175
159
  @toggleDropdownExplanationImageLongAlt="toggleDropdownExplanationImageLongAlt"
176
160
  @click="handleClick"
177
161
  >
@@ -186,7 +170,7 @@
186
170
  <template v-if="!globalMetrics">
187
171
  <div
188
172
  v-if="!showAnswers"
189
- v-breakpoint:questionEl="breakpoints"
173
+ v-breakpoint="breakpointsWithEl"
190
174
  v-dark="isDarkMode"
191
175
  :tabindex="showAnswers ? -1 : 0"
192
176
  class="uikit-question-choices-container__strikethrough"
@@ -261,7 +245,7 @@
261
245
  </div>
262
246
  </template>
263
247
  <div
264
- v-breakpoint:questionEl="breakpoints"
248
+ v-breakpoint="breakpointsWithEl"
265
249
  class="uikit-question-choices-container__motivational-moment"
266
250
  :class="{
267
251
  'uikit-question-choices-container__motivational-moment--mcr': isMCR,
@@ -286,133 +270,116 @@
286
270
  </div>
287
271
  </template>
288
272
 
289
- <script lang="ts">
290
- import { Component, Vue, Prop, Emit } from 'vue-facing-decorator'
291
- import type { Study } from '@pocketprep/types'
273
+ <script setup lang="ts">
292
274
  import DropdownExplanation from './DropdownExplanation.vue'
293
275
  import Icon from '../../Icons/Icon.vue'
294
276
  import PocketButton from '../../Buttons/Button.vue'
295
- import { dark, breakpoint } from '../../../directives'
296
- import type { TBreakPointsObject, TChoice, TChoiceKey, TChoiceScores } from './../question'
297
-
298
- @Component({
299
- components: {
300
- Icon,
301
- PocketButton,
302
- DropdownExplanation,
303
- },
304
- directives: {
305
- dark,
306
- breakpoint,
307
- },
308
- })
309
- export default class ChoicesContainer extends Vue {
310
- @Prop() question!: Study.Class.QuestionJSON
311
- @Prop({ default: [] }) choices!: TChoice[]
312
- @Prop({ default: false }) showAnswers!: boolean
313
- @Prop({ default: false }) showExplanation!: boolean
314
- @Prop({ default: false }) isMCR!: boolean
315
- @Prop({ default: false }) isUnanswered!: boolean
316
- @Prop({ default: [] }) answerKeys!: TChoiceKey[]
317
- @Prop({ default: null }) hoverChoiceKey!: TChoiceKey | null
318
- @Prop({ default: null }) focusChoiceKey!: TChoiceKey | null
319
- @Prop({ default: [] }) selectedChoices!: TChoiceKey[]
320
- @Prop({ default: [] }) distractorKeys!: TChoiceKey[]
321
- @Prop({ default: [] }) choiceStrikes!: TChoiceKey[]
322
- @Prop() choiceScores!: TChoiceScores
323
- @Prop({ default: null }) passageImageUrl!: string | null
324
- @Prop({ default: false }) isCorrect!: boolean
325
- @Prop({ default: null }) globalMetrics!: Study.Class.GlobalQuestionMetricJSON | null
326
- @Prop({ default: false }) reviewMode!: boolean
327
- @Prop({ default: false }) showExplanationImageLongAlt!: boolean
328
- @Prop({ default: null }) explanationImageUrl!: string | null
329
- @Prop({ default: undefined }) explanationImageAlt!: string | undefined
330
- @Prop({ default: undefined }) explanationImageLongAlt!: string | undefined
331
- @Prop({ default: undefined }) reference!: string | undefined
332
- @Prop({ default: false }) hideReferences!: boolean
333
- @Prop({ default: false }) isDarkMode!: boolean
334
- @Prop({ default: null }) questionEl!: Element | null
335
- @Prop({ default: {
336
- 'mobile': 767,
337
- 'tablet-portrait': 1023,
338
- 'tablet-landscape': 1439,
339
- } }) breakpoints!: TBreakPointsObject
340
- @Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
341
-
342
- stripHtmlTags (string?: string) {
343
- if (string) {
344
- const div = document.createElement('div')
345
- div.innerHTML = string
346
- return div.textContent || ''
347
- }
348
- return ''
277
+ import { dark as vDark, breakpoint as vBreakpoint } from '../../../directives'
278
+ import { useQuestionContext } from './composables'
279
+ import type { TChoiceKey } from '../question'
280
+
281
+ const emit = defineEmits<{
282
+ 'emitChoiceFocusIn': [ choiceKey: TChoiceKey ]
283
+ 'emitChoiceFocusOut': [ focusEvent: FocusEvent]
284
+ 'emitChoiceMouseOver': [ choiceKey: TChoiceKey ]
285
+ 'emitChoiceMouseLeave': []
286
+ 'emitHandleTouchStart': [ touchEvent: TouchEvent ]
287
+ 'emitHandleTouchMove': [ touchEvent: TouchEvent ]
288
+ 'emitHandleTouchEnd': [ touchEndPayload: { choiceKey: TChoiceKey; event: TouchEvent } ]
289
+ 'emitSelectedChoice': [ choiceKey: TChoiceKey ]
290
+ 'emitClickChoiceStrike': [ choiceKey: TChoiceKey ]
291
+ 'toggleChoiceExplanation': []
292
+ 'toggleDropdownExplanationImageLongAlt': []
293
+ 'click': [ event: MouseEvent ]
294
+ }>()
295
+
296
+ const {
297
+ // questionEl is used by the breakpoint directive
298
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
299
+ questionEl,
300
+ question,
301
+ choices,
302
+ showAnswers,
303
+ showExplanation,
304
+ isMCR,
305
+ isUnanswered,
306
+ answerKeys,
307
+ hoverChoiceKey,
308
+ focusChoiceKey,
309
+ selectedChoices,
310
+ distractorKeys,
311
+ choiceStrikes,
312
+ choiceScores,
313
+ globalMetrics,
314
+ reviewMode,
315
+ isDarkMode,
316
+ passageImageUrl,
317
+ isCorrect,
318
+ breakpointsWithEl,
319
+ } = useQuestionContext()
320
+
321
+ const stripHtmlTags = (string?: string) => {
322
+ if (string) {
323
+ const div = document.createElement('div')
324
+ div.innerHTML = string
325
+ return div.textContent || ''
349
326
  }
327
+ return ''
328
+ }
350
329
 
351
- @Emit('emitChoiceFocusIn')
352
- emitChoiceFocusIn (choiceKey: TChoiceKey) {
353
- return choiceKey
354
- }
330
+ const emitChoiceFocusIn = (choiceKey: TChoiceKey) => {
331
+ emit('emitChoiceFocusIn', choiceKey)
332
+ }
355
333
 
356
- @Emit('emitChoiceFocusOut')
357
- emitChoiceFocusOut (event: FocusEvent) {
358
- return event
359
- }
334
+ const emitChoiceFocusOut = (focusEvent: FocusEvent) => {
335
+ emit('emitChoiceFocusOut', focusEvent)
336
+ }
360
337
 
361
- @Emit('emitChoiceMouseOver')
362
- emitChoiceMouseOver (choiceKey: TChoiceKey) {
363
- return choiceKey
364
- }
338
+ const emitChoiceMouseOver = (choiceKey: TChoiceKey) => {
339
+ emit('emitChoiceMouseOver', choiceKey)
340
+ }
365
341
 
366
- @Emit('emitChoiceMouseLeave')
367
- emitChoiceMouseLeave () {
368
- return
369
- }
342
+ const emitChoiceMouseLeave = () => {
343
+ emit('emitChoiceMouseLeave')
344
+ }
370
345
 
371
- @Emit('emitHandleTouchStart')
372
- emitHandleTouchStart (event: TouchEvent) {
373
- return event
374
- }
346
+ const emitHandleTouchStart = (event: TouchEvent) => {
347
+ emit('emitHandleTouchStart', event)
348
+ }
375
349
 
376
- @Emit('emitHandleTouchMove')
377
- emitHandleTouchMove (event: TouchEvent) {
378
- return event
379
- }
350
+ const emitHandleTouchMove = (event: TouchEvent) => {
351
+ emit('emitHandleTouchMove', event)
352
+ }
380
353
 
381
- @Emit('emitHandleTouchEnd')
382
- emitHandleTouchEnd (choiceKey: TChoiceKey, event: TouchEvent) {
383
- return { choiceKey, event }
384
- }
354
+ const emitHandleTouchEnd = (choiceKey: TChoiceKey, event: TouchEvent) => {
355
+ emit('emitHandleTouchEnd', { choiceKey, event })
356
+ }
385
357
 
386
- @Emit('emitSelectedChoice')
387
- emitSelectedChoice (choiceKey: TChoiceKey) {
388
- return choiceKey
389
- }
358
+ const emitSelectedChoice = (choiceKey: TChoiceKey) => {
359
+ emit('emitSelectedChoice', choiceKey)
360
+ }
390
361
 
391
- @Emit('emitClickChoiceStrike')
392
- emitClickChoiceStrike (choiceKey: TChoiceKey) {
393
- return choiceKey
394
- }
362
+ const emitClickChoiceStrike = (choiceKey: TChoiceKey) => {
363
+ emit('emitClickChoiceStrike', choiceKey)
364
+ }
395
365
 
396
- @Emit('toggleChoiceExplanation')
397
- toggleChoiceExplanation () {
398
- return
399
- }
366
+ const toggleChoiceExplanation = () => {
367
+ emit('toggleChoiceExplanation')
368
+ }
400
369
 
401
- @Emit('toggleDropdownExplanationImageLongAlt')
402
- toggleDropdownExplanationImageLongAlt () {
403
- return
404
- }
370
+ const toggleDropdownExplanationImageLongAlt = () => {
371
+ emit('toggleDropdownExplanationImageLongAlt')
372
+ }
405
373
 
406
- @Emit('click')
407
- handleClick (event: MouseEvent) {
408
- return event
409
- }
374
+ const handleClick = (event: MouseEvent) => {
375
+ emit('click', event)
410
376
  }
411
377
  </script>
412
378
 
413
379
  <style lang="scss">
414
- @import '../../../styles/colors';
415
- @import '../../../styles/breakpoints';
380
+ @use 'sass:color';
381
+ @use '@/styles/breakpoints' as *;
382
+ @use '@/styles/colors' as *;
416
383
 
417
384
  .uikit-question-choices-container {
418
385
  display: contents;
@@ -848,7 +815,7 @@ export default class ChoicesContainer extends Vue {
848
815
  color: $white;
849
816
 
850
817
  &:hover {
851
- background-color: mix($brand-blue, black, 90%);
818
+ background-color: color.mix($brand-blue, black, 90%);
852
819
  }
853
820
 
854
821
  &:focus {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- v-breakpoint:questionEl="breakpoints"
3
+ v-breakpoint="breakpointsWithEl"
4
4
  ref="uikit-question-dropdown-explanation"
5
5
  class="uikit-question-dropdown-explanation__dropdown-explanation"
6
6
  :class="{
@@ -22,7 +22,7 @@
22
22
  >
23
23
  <PocketButton
24
24
  v-if="explanationImageLongAlt"
25
- v-breakpoint:questionEl="breakpoints"
25
+ v-breakpoint="breakpointsWithEl"
26
26
  type="tertiary-small"
27
27
  class="uikit-question-dropdown-explanation__toggle-dropdown-explanation-img-description"
28
28
  :class="{
@@ -52,7 +52,7 @@
52
52
  v-if="showExplanationImageLongAlt"
53
53
  ref="uikit-question-dropdown-explanation__dropdown-explanation-img-description"
54
54
  v-dark="isDarkMode"
55
- v-breakpoint:questionEl="breakpoints"
55
+ v-breakpoint="breakpointsWithEl"
56
56
  class="uikit-question-dropdown-explanation__dropdown-explanation-img-description"
57
57
  tabindex="-1"
58
58
  v-html="explanationImageLongAlt"
@@ -69,66 +69,52 @@
69
69
  </div>
70
70
  </template>
71
71
 
72
- <script lang="ts">
73
- import { Component, Emit, Prop, Vue } from 'vue-facing-decorator'
74
- import type { Study } from '@pocketprep/types'
72
+ <script setup lang="ts">
73
+ import { computed } from 'vue'
75
74
  import Icon from '../../Icons/Icon.vue'
76
75
  import PocketButton from '../../Buttons/Button.vue'
77
- import { breakpoint, dark } from '../../../directives'
78
- import type { TBreakPointsObject, TChoice, TChoiceKey } from './../question'
76
+ import { dark as vDark, breakpoint as vBreakpoint } from '../../../directives'
79
77
  import { highlightKeywordsInText } from '../../../utils'
80
-
81
- @Component({
82
- components: {
83
- Icon,
84
- PocketButton,
85
- },
86
- directives: {
87
- dark,
88
- breakpoint,
89
- },
78
+ import { useQuestionContext } from './composables'
79
+
80
+ const emit = defineEmits<{
81
+ 'toggleDropdownExplanationImageLongAlt': []
82
+ }>()
83
+
84
+ const {
85
+ // questionEl is used by the breakpoint directive
86
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
87
+ questionEl,
88
+ question,
89
+ globalMetrics,
90
+ showExplanationImageLongAlt,
91
+ explanationImageUrl,
92
+ explanationImageAlt,
93
+ explanationImageLongAlt,
94
+ reference,
95
+ hideReferences,
96
+ isDarkMode,
97
+ breakpointsWithEl,
98
+ keywordDefinitions,
99
+ } = useQuestionContext()
100
+
101
+ const explanation = computed(() => {
102
+ return highlightKeywordsInText({
103
+ text: question.value.explanation || '',
104
+ keywordDefinitions: keywordDefinitions.value,
105
+ isDarkMode: isDarkMode.value,
106
+ location: 'explanation',
107
+ })
90
108
  })
91
- export default class DropdownExplanation extends Vue {
92
- @Prop() question!: Study.Class.QuestionJSON
93
- @Prop({ default: [] }) answerKeys!: TChoiceKey[]
94
- @Prop() choice!: TChoice
95
- @Prop({ default: null }) globalMetrics!: Study.Class.GlobalQuestionMetricJSON | null
96
- @Prop({ default: false }) showAnswers!: boolean
97
- @Prop({ default: false }) isMCR!: boolean
98
- @Prop({ default: false }) showExplanationImageLongAlt!: boolean
99
- @Prop({ default: null }) explanationImageUrl!: string | null
100
- @Prop({ default: undefined }) explanationImageAlt!: string | undefined
101
- @Prop({ default: undefined }) explanationImageLongAlt!: string | undefined
102
- @Prop({ default: undefined }) reference!: string | undefined
103
- @Prop({ default: false }) hideReferences!: boolean
104
- @Prop({ default: false }) isDarkMode!: boolean
105
- @Prop({ default: null }) questionEl!: Element | null
106
- @Prop({ default: {
107
- 'mobile': 767,
108
- 'tablet-portrait': 1023,
109
- 'tablet-landscape': 1439,
110
- } }) breakpoints!: TBreakPointsObject
111
- @Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
112
-
113
- get explanation () {
114
- return highlightKeywordsInText({
115
- text: this.question.explanation || '',
116
- keywordDefinitions: this.keywordDefinitions,
117
- isDarkMode: this.isDarkMode,
118
- location: 'explanation',
119
- })
120
- }
121
-
122
- @Emit('toggleDropdownExplanationImageLongAlt')
123
- toggleDropdownExplanationImageLongAlt () {
124
- return
125
- }
109
+
110
+ const toggleDropdownExplanationImageLongAlt = () => {
111
+ emit('toggleDropdownExplanationImageLongAlt')
126
112
  }
127
113
  </script>
128
114
 
129
115
  <style lang="scss">
130
- @import '../../../styles/colors';
131
- @import '../../../styles/breakpoints';
116
+ @use '@/styles/breakpoints' as *;
117
+ @use '@/styles/colors' as *;
132
118
 
133
119
  .uikit-question-dropdown-explanation {
134
120
  &__dropdown-explanation {
@@ -1,12 +1,12 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="showExplanation && !showPaywall"
4
- v-breakpoint:questionEl="breakpoints"
4
+ v-breakpoint="breakpointsWithEl"
5
5
  v-dark="isDarkMode"
6
6
  class="uikit-question-explanation"
7
7
  >
8
8
  <div
9
- ref="explanation"
9
+ ref="explanationRef"
10
10
  v-dark="isDarkMode"
11
11
  class="uikit-question-explanation__explanation-title"
12
12
  tabindex="-1"
@@ -16,14 +16,14 @@
16
16
  <slot name="explanationTopExperiment" />
17
17
  <div
18
18
  v-dark="isDarkMode"
19
- v-breakpoint:questionEl="breakpoints"
19
+ v-breakpoint="breakpointsWithEl"
20
20
  class="uikit-question-explanation__explanation-text"
21
21
  v-html="explanation"
22
22
  />
23
23
  <img
24
24
  v-if="explanationImageUrl"
25
25
  v-dark="isDarkMode"
26
- v-breakpoint:questionEl="breakpoints"
26
+ v-breakpoint="breakpointsWithEl"
27
27
  class="uikit-question-explanation__explanation-image"
28
28
  :class="{
29
29
  'uikit-question-explanation__explanation-image--long-alt': explanationImageLongAlt,
@@ -33,7 +33,7 @@
33
33
  >
34
34
  <PocketButton
35
35
  v-if="explanationImageLongAlt"
36
- v-breakpoint:questionEl="breakpoints"
36
+ v-breakpoint="breakpointsWithEl"
37
37
  type="tertiary-small"
38
38
  class="uikit-question-explanation__toggle-explanation-img-description"
39
39
  :class="{
@@ -87,69 +87,59 @@
87
87
  </div>
88
88
  </template>
89
89
 
90
- <script lang="ts">
91
- import { Component, Emit, Prop, Vue } from 'vue-facing-decorator'
92
- import type { Study } from '@pocketprep/types'
90
+ <script setup lang="ts">
91
+ import { computed } from 'vue'
93
92
  import Icon from '../../Icons/Icon.vue'
94
93
  import PocketButton from '../../Buttons/Button.vue'
95
- import { breakpoint, dark } from '../../../directives'
96
- import type { TBreakPointsObject } from './../question'
94
+ import { dark as vDark, breakpoint as vBreakpoint } from '../../../directives'
97
95
  import { highlightKeywordsInText } from '../../../utils'
98
-
99
- @Component({
100
- components: {
101
- Icon,
102
- PocketButton,
103
- },
104
- directives: {
105
- dark,
106
- breakpoint,
107
- },
96
+ import { useQuestionContext } from './composables'
97
+
98
+ const emit = defineEmits<{
99
+ 'toggleExplanationImageLongAlt': []
100
+ 'toggleExplanation': []
101
+ }>()
102
+
103
+ const {
104
+ // questionEl is used by the breakpoint directive
105
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
106
+ questionEl,
107
+ question,
108
+ showExplanation,
109
+ showExplanationImageLongAlt,
110
+ showPaywall,
111
+ explanationImageUrl,
112
+ explanationImageAlt,
113
+ explanationImageLongAlt,
114
+ reference,
115
+ hideReferences,
116
+ isDarkMode,
117
+ reviewMode,
118
+ breakpointsWithEl,
119
+ keywordDefinitions,
120
+ } = useQuestionContext()
121
+
122
+ const explanation = computed(() => {
123
+ return highlightKeywordsInText({
124
+ text: question.value.explanation || '',
125
+ keywordDefinitions: keywordDefinitions.value,
126
+ isDarkMode: isDarkMode.value,
127
+ location: 'explanation',
128
+ })
108
129
  })
109
- export default class Explanation extends Vue {
110
- @Prop() question!: Study.Class.QuestionJSON
111
- @Prop({ default: false }) showExplanation!: boolean
112
- @Prop({ default: false }) showPaywall!: boolean
113
- @Prop({ default: false }) showExplanationImageLongAlt!: boolean
114
- @Prop({ default: null }) explanationImageUrl!: string | null
115
- @Prop({ default: undefined }) explanationImageAlt!: string | undefined
116
- @Prop({ default: undefined }) explanationImageLongAlt!: string | undefined
117
- @Prop({ default: false }) reviewMode!: boolean
118
- @Prop({ default: undefined }) reference!: string | undefined
119
- @Prop({ default: false }) hideReferences!: boolean
120
- @Prop({ default: false }) isDarkMode!: boolean
121
- @Prop({ default: null }) questionEl!: Element | null
122
- @Prop({ default: {
123
- 'mobile': 767,
124
- 'tablet-portrait': 1023,
125
- 'tablet-landscape': 1439,
126
- } }) breakpoints!: TBreakPointsObject
127
- @Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
128
-
129
- get explanation () {
130
- return highlightKeywordsInText({
131
- text: this.question.explanation || '',
132
- keywordDefinitions: this.keywordDefinitions,
133
- isDarkMode: this.isDarkMode,
134
- location: 'explanation',
135
- })
136
- }
137
-
138
- @Emit('toggleExplanationImageLongAlt')
139
- toggleExplanationImageLongAlt () {
140
- return
141
- }
142
130
 
143
- @Emit('toggleExplanation')
144
- toggleExplanation () {
145
- return
146
- }
131
+ const toggleExplanationImageLongAlt = () => {
132
+ emit('toggleExplanationImageLongAlt')
133
+ }
134
+
135
+ const toggleExplanation = () => {
136
+ emit('toggleExplanation')
147
137
  }
148
138
  </script>
149
139
 
150
140
  <style lang="scss">
151
- @import '../../../styles/colors';
152
- @import '../../../styles/breakpoints';
141
+ @use '@/styles/breakpoints' as *;
142
+ @use '@/styles/colors' as *;
153
143
 
154
144
  .uikit-question-explanation {
155
145
  position: relative;