@pocketprep/ui-kit 3.7.0 → 3.7.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.
@@ -12,10 +12,10 @@
12
12
  {{ announcementMessage }}
13
13
  </div>
14
14
  <div
15
- v-breakpoint="breakpointsWithEl"
16
15
  class="uikit-question-build-list-choices-container-wrapper"
17
16
  >
18
17
  <TransitionGroup
18
+ v-dark="isDarkMode"
19
19
  v-breakpoint="breakpointsWithEl"
20
20
  name="list"
21
21
  tag="ol"
@@ -27,6 +27,7 @@
27
27
  (showBuildListOrder || reviewMode) && !isBuildListOrderCorrect,
28
28
  'uikit-question-build-list-choices-container--teach-group-review':
29
29
  (showBuildListOrder || reviewMode) && isTeachGroupReview,
30
+ 'uikit-question-build-list-choices-container--review-mode': reviewMode,
30
31
  }"
31
32
  role="list"
32
33
  :aria-label="`Build list with ${orderedChoices.length} items`"
@@ -46,6 +47,9 @@
46
47
  v-dark="isDarkMode"
47
48
  v-breakpoint="breakpointsWithEl"
48
49
  class="uikit-question-build-list-choices-container__choice-number"
50
+ :class="{
51
+ 'uikit-question-build-list-choices-container__choice-number--review-mode': reviewMode
52
+ }"
49
53
  >
50
54
  {{ index + 1 }}
51
55
  </div>
@@ -54,14 +58,14 @@
54
58
  v-breakpoint="breakpointsWithEl"
55
59
  class="uikit-question-build-list-choices-container__choice"
56
60
  :class="{
57
- 'uikit-question-build-list-choices-container__choice--showing-answer':
58
- reviewMode || showBuildListOrder
61
+ 'uikit-question-build-list-choices-container__choice--review-mode': reviewMode
59
62
  }"
60
63
  role="group"
61
64
  :aria-label="`Choice ${index + 1}: ${stripHtmlTags(choice.text || '')}`"
62
- :tabindex="(showBuildListOrder || reviewMode) ? -1 : 0"
65
+ :tabindex="-1"
63
66
  @keydown="handleCardKeydown($event, index)"
64
- @click="handleCardClick($event)"
67
+ @click.prevent
68
+ @mousedown.prevent
65
69
  :ref="(el) => setCardRef(el, choice.key)"
66
70
  >
67
71
  <div
@@ -72,7 +76,9 @@
72
76
  'uikit-question-build-list-choices-container__mobile-choice-number--correct':
73
77
  (reviewMode || showBuildListOrder) && isChoiceInCorrectPosition(choice, index),
74
78
  'uikit-question-build-list-choices-container__mobile-choice-number--incorrect':
75
- (reviewMode || showBuildListOrder) && !isChoiceInCorrectPosition(choice, index)
79
+ (reviewMode || showBuildListOrder) && !isChoiceInCorrectPosition(choice, index),
80
+ 'uikit-question-build-list-choices-container__mobile-choice-number--review-mode':
81
+ reviewMode,
76
82
  }"
77
83
  >
78
84
  {{ index + 1 }}
@@ -92,7 +98,7 @@
92
98
  {{ getCorrectPositionOrderNumber(choice) }}
93
99
  </div>
94
100
  <Icon
95
- v-if="isChoiceInCorrectPosition(choice, index)"
101
+ v-if="isChoiceInCorrectPosition(choice, index) && !reviewMode"
96
102
  v-dark="isDarkMode"
97
103
  v-breakpoint="breakpointsWithEl"
98
104
  class="uikit-question-build-list-choices-container__answer-correct-icon"
@@ -100,7 +106,7 @@
100
106
 
101
107
  />
102
108
  <Icon
103
- v-else
109
+ v-else-if="!isChoiceInCorrectPosition(choice, index) && !reviewMode"
104
110
  v-dark="isDarkMode"
105
111
  v-breakpoint="breakpointsWithEl"
106
112
  class="uikit-question-build-list-choices-container__answer-incorrect-icon"
@@ -119,7 +125,8 @@
119
125
  index === 0
120
126
  }"
121
127
  :disabled="index === 0"
122
- @click="moveChoiceUp(index)"
128
+ :tabindex="(showBuildListOrder || reviewMode) ? -1 : 0"
129
+ @click.prevent.stop="moveChoiceUp(index)"
123
130
  :aria-label="`Move '${stripHtmlTags(choice.text || '')}' up`"
124
131
  :title="'Move up'"
125
132
  >
@@ -138,7 +145,8 @@
138
145
  index === orderedChoices.length - 1
139
146
  }"
140
147
  :disabled="index === orderedChoices.length - 1"
141
- @click="moveChoiceDown(index)"
148
+ :tabindex="(showBuildListOrder || reviewMode) ? -1 : 0"
149
+ @click.prevent.stop="moveChoiceDown(index)"
142
150
  :aria-label="`Move '${stripHtmlTags(choice.text || '')}' down`"
143
151
  :title="'Move down'"
144
152
  >
@@ -187,6 +195,7 @@ const orderedChoices = ref<TBuildListChoice[]>([])
187
195
  const floatingChoiceKey = ref<string | null>(null)
188
196
  const choiceRefs = ref<Map<string, HTMLDivElement>>(new Map())
189
197
  const announcementMessage = ref<string>('')
198
+ const isMoveTriggeredByArrowKey = ref<boolean>(false)
190
199
 
191
200
  onMounted(() => {
192
201
  updateOrderedChoices()
@@ -230,11 +239,37 @@ const restoreFocusAfterMove = (movedChoiceKey: string) => {
230
239
  setTimeout(() => {
231
240
  const choiceEl = choiceRefs.value.get(movedChoiceKey)
232
241
  if (choiceEl) {
242
+ // Temporarily make the choice focusable for animation
243
+ choiceEl.setAttribute('tabindex', '0')
233
244
  choiceEl.focus()
245
+
246
+ // Remove tabindex after animation completes
247
+ const animationDelay = prefersReducedMotion() ? 0 : 350
248
+ setTimeout(() => {
249
+ choiceEl.setAttribute('tabindex', '-1')
250
+ }, animationDelay)
234
251
  }
235
252
  }, delay)
236
253
  }
237
254
 
255
+ const removeFocusAfterAnimation = (movedChoiceKey: string) => {
256
+ // Don't remove focus if we're in review mode or showing the build list order
257
+ if (showBuildListOrder.value || reviewMode.value) {
258
+ return
259
+ }
260
+
261
+ // Only remove focus if the move was NOT triggered by arrow keys
262
+ if (!isMoveTriggeredByArrowKey.value) {
263
+ const animationDelay = prefersReducedMotion() ? 0 : 350 // Slightly after animation completes
264
+ setTimeout(() => {
265
+ const choiceEl = choiceRefs.value.get(movedChoiceKey)
266
+ if (choiceEl) {
267
+ choiceEl.blur()
268
+ }
269
+ }, animationDelay)
270
+ }
271
+ }
272
+
238
273
  // For accessibility, we want to announce to the user where they moved the choice
239
274
  // to in the ordered list. ie: “Moved Photosynthesis to position 2 of 6.”
240
275
  const announceMove = (choiceText: string, newPosition: number, totalItems: number) => {
@@ -268,6 +303,9 @@ const moveChoiceUp = (index: number) => {
268
303
  // Restore focus to the moved card
269
304
  restoreFocusAfterMove(currentItem.key)
270
305
 
306
+ // Remove focus after animation if not triggered by arrow key
307
+ removeFocusAfterAnimation(currentItem.key)
308
+
271
309
  // Clear floating state after animation completes
272
310
  const animationDelay = prefersReducedMotion() ? 0 : 300
273
311
  setTimeout(() => {
@@ -298,6 +336,9 @@ const moveChoiceDown = (index: number) => {
298
336
  // Restore focus to the moved card
299
337
  restoreFocusAfterMove(currentItem.key)
300
338
 
339
+ // Remove focus after animation
340
+ removeFocusAfterAnimation(currentItem.key)
341
+
301
342
  // Clear floating state after animation completes
302
343
  const animationDelay = prefersReducedMotion() ? 0 : 300
303
344
  setTimeout(() => {
@@ -321,32 +362,39 @@ const isChoiceInCorrectPosition = (choice: TBuildListChoice, currentIndex: numbe
321
362
  return correctOrderPosition === currentOrderPosition
322
363
  }
323
364
 
324
- const handleCardClick = (event: MouseEvent) => {
325
- if (showBuildListOrder.value || reviewMode.value) {
326
- event.preventDefault()
327
- event.stopPropagation()
328
- }
329
- }
330
-
331
365
  const handleCardKeydown = (event: KeyboardEvent, index: number) => {
332
366
  // Handle Alt+Arrow shortcuts for moving cards
333
367
  if (event.altKey) {
334
368
  if (event.key === 'ArrowUp' || event.key === 'Up') {
335
369
  event.preventDefault()
370
+ isMoveTriggeredByArrowKey.value = true
336
371
  moveChoiceUp(index)
372
+ // Reset flag after a short delay
373
+ setTimeout(() => {
374
+ isMoveTriggeredByArrowKey.value = false
375
+ }, 100)
337
376
  } else if (event.key === 'ArrowDown' || event.key === 'Down') {
338
377
  event.preventDefault()
378
+ isMoveTriggeredByArrowKey.value = true
339
379
  moveChoiceDown(index)
380
+ // Reset flag after a short delay
381
+ setTimeout(() => {
382
+ isMoveTriggeredByArrowKey.value = false
383
+ }, 100)
340
384
  }
341
385
  }
342
386
  }
343
387
 
344
-
345
388
  watch(orderedChoices, () => {
346
389
  const newChoices = orderedChoices.value
347
390
  emit('reorderBuildList', newChoices)
348
391
  }, { deep: true })
349
392
 
393
+ watch(selectedBuildListChoiceOrder, () => {
394
+ if ((reviewMode.value || showBuildListOrder.value)) {
395
+ updateOrderedChoices()
396
+ }
397
+ })
350
398
  </script>
351
399
 
352
400
  <style lang="scss">
@@ -378,22 +426,23 @@ watch(orderedChoices, () => {
378
426
  flex-direction: column;
379
427
  gap: 12px;
380
428
  list-style: none;
381
- margin: 0;
429
+ margin: 0 0 0 -46px;
382
430
  padding: 0;
383
431
 
384
432
  &::after {
385
- content: '';
386
- position: absolute;
387
- top: -8px;
388
- bottom: -8px;
389
- left: 32px;
390
- right: -8px;
391
- border-radius: 11px;
392
- pointer-events: none;
433
+ content: '';
434
+ position: absolute;
435
+ top: -8px;
436
+ bottom: -8px;
437
+ left: 8px;
438
+ right: 10px;
439
+ border-radius: 11px;
440
+ pointer-events: none;
393
441
 
394
- }
442
+ }
395
443
 
396
444
  &--mobile {
445
+ margin: 0;
397
446
  &::after {
398
447
  content: '';
399
448
  position: absolute;
@@ -407,6 +456,17 @@ watch(orderedChoices, () => {
407
456
  }
408
457
  }
409
458
 
459
+ &--review-mode:not(&--mobile) {
460
+ margin: 0;
461
+ max-width: 492px;
462
+ width: 100%;
463
+
464
+ &::after {
465
+ left: -8px;
466
+ right: -8px;
467
+ }
468
+ }
469
+
410
470
  &--correct {
411
471
  &::after {
412
472
  display: block;
@@ -450,8 +510,12 @@ watch(orderedChoices, () => {
450
510
  z-index: 10;
451
511
 
452
512
  .uikit-question-build-list-choices-container__choice {
453
- box-shadow: 0 8px 24px 0 rgba(71, 89, 103, 0.4);
454
- border-radius: 8px;
513
+ outline: 2px solid $brand-blue;
514
+ outline-offset: 2px;
515
+
516
+ &--dark {
517
+ outline-color: $banana-bread;
518
+ }
455
519
  }
456
520
  }
457
521
  }
@@ -459,7 +523,7 @@ watch(orderedChoices, () => {
459
523
  &__choice-number,
460
524
  &__mobile-choice-number {
461
525
  display: flex;
462
- align-items: center;
526
+ align-items: flex-end;
463
527
  justify-content: center;
464
528
  width: 24px;
465
529
  height: 24px;
@@ -477,13 +541,14 @@ watch(orderedChoices, () => {
477
541
  }
478
542
 
479
543
  &__choice-number {
544
+ &--review-mode,
480
545
  &--mobile {
481
546
  display: none;
482
547
  }
483
548
  }
484
549
 
485
550
  &__mobile-choice-number {
486
- &:not(&--mobile) {
551
+ &:not(&--mobile, &--review-mode) {
487
552
  display: none;
488
553
  }
489
554
 
@@ -524,28 +589,15 @@ watch(orderedChoices, () => {
524
589
  box-shadow: 0 1px 4px 0 rgba(71, 89, 103, 0.30);
525
590
  outline: none;
526
591
 
527
- &:focus:not(&--showing-answer) {
528
- outline: 2px solid $brand-blue;
529
- outline-offset: 2px;
530
- }
531
-
532
592
  &--dark {
533
593
  border: 1px solid $slate;
534
594
  background-color: $brand-black;
535
595
  box-shadow: 0 1px 4px 0 rgba(71, 89, 103, 0.30);
536
-
537
- &:focus:not(&--showing-answer) {
538
- outline-color: $banana-bread;
539
- }
540
596
  }
541
597
 
542
598
  &--mobile {
543
599
  width: 100%;
544
600
  }
545
-
546
- &--showing-answer {
547
- gap: 27px;
548
- }
549
601
  }
550
602
 
551
603
  &__choice-text {
@@ -589,9 +641,30 @@ watch(orderedChoices, () => {
589
641
  cursor: pointer;
590
642
  transition: all 0.2s ease;
591
643
 
644
+ &:focus {
645
+ outline: none;
646
+ border: 2px solid $white;
647
+ box-shadow: 0 0 0 2px $brand-blue-hover;
648
+ }
649
+
650
+ &:hover,
651
+ &:focus {
652
+ background-color: $brand-blue-hover;
653
+ }
654
+
592
655
  &--dark {
593
656
  background-color: $banana-bread;
594
657
  color: $brand-black;
658
+
659
+ &:focus {
660
+ border: 2px solid $brand-black;
661
+ box-shadow: 0 0 0 2px $butterscotch;
662
+ }
663
+
664
+ &:hover,
665
+ &:focus {
666
+ background-color: $butterscotch;
667
+ }
595
668
  }
596
669
 
597
670
  &--disabled {
@@ -9,8 +9,8 @@
9
9
  'uikit-question-summary--matrix-question-review-mode': isMatrixQuestion && reviewMode,
10
10
  'uikit-question-summary--mpmc-question': isMPMCQuestion,
11
11
  'uikit-question-summary--mpmc-question-review-mode': isMPMCQuestion && reviewMode,
12
- 'uikit-question-summary--build-list-question': isBuildListQuestion,
13
- 'uikit-question-summary--build-list-question-review-mode': isBuildListQuestion && reviewMode,
12
+ 'uikit-question-summary--build-list-question-not-review-mode':
13
+ isBuildListQuestion && !reviewMode,
14
14
  }"
15
15
  >
16
16
  <div class="uikit-question-summary__summary-title">
@@ -218,10 +218,8 @@ const toggleSummaryExplanationImageLongAlt = () => {
218
218
  max-width: 518px;
219
219
  }
220
220
 
221
- &--build-list-question {
221
+ &--build-list-question-not-review-mode {
222
222
  max-width: 452px;
223
- margin-left: 38px;
224
- display: inline-block;
225
223
  }
226
224
 
227
225
  &--matrix-question-review-mode#{&}--tablet-landscape,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pocketprep/ui-kit",
3
- "version": "3.7.0",
3
+ "version": "3.7.1",
4
4
  "description": "Pocket Prep UI Kit",
5
5
  "author": "pocketprep",
6
6
  "scripts": {