@pocketprep/ui-kit 3.0.29 → 3.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@pocketprep/ui-kit.js +3027 -2985
- package/dist/@pocketprep/ui-kit.js.map +1 -1
- package/dist/@pocketprep/ui-kit.umd.cjs +9 -9
- package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/lib/components/Modal/ModalContainer.vue +54 -49
- package/lib/components/Quiz/Question.vue +102 -0
- package/package.json +1 -1
|
@@ -54,60 +54,65 @@ export default class ModalContainer extends Vue {
|
|
|
54
54
|
savedYPosition = 0
|
|
55
55
|
|
|
56
56
|
mounted () {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
57
|
+
// Prevent an error where multiple modals trigger a loop
|
|
58
|
+
// TODO: Find a way to have only the latest modal's focusListener active
|
|
59
|
+
const openModals = document.querySelectorAll('.uikit-modal-container')
|
|
60
|
+
if (openModals.length === 1) {
|
|
61
|
+
const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]'
|
|
62
|
+
|
|
63
|
+
// Reset focus to last element in modal so next tab will move it to top
|
|
64
|
+
const modalContainerEl = this.$refs['modalContainer'] as HTMLElement
|
|
65
|
+
const modalFocusableEls = Array.from<HTMLElement>(modalContainerEl.querySelectorAll(focusableSelectors))
|
|
66
|
+
if (modalFocusableEls.length) {
|
|
67
|
+
modalFocusableEls[modalFocusableEls.length - 1]?.focus()
|
|
68
|
+
modalFocusableEls[modalFocusableEls.length - 1]?.blur()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Trap the user's focus within the modal - don't allow focusing elements behind the overlay
|
|
72
|
+
this.focusListener = event => {
|
|
73
|
+
const target = (event as FocusEvent).target as HTMLElement // The element receiving focus
|
|
74
|
+
const isFocusOutside = target && modalContainerEl && !modalContainerEl.contains(target)
|
|
75
|
+
const hasCalendarClass = target
|
|
76
|
+
&& Array.from(target.classList).find(
|
|
77
|
+
c => c === 'button-next-month' || c === 'button-previous-month' || c === 'day-item'
|
|
78
|
+
)
|
|
79
|
+
if (isFocusOutside && !hasCalendarClass) {
|
|
80
|
+
const focusableModalChildren = Array.from<HTMLElement>(modalContainerEl.querySelectorAll(
|
|
81
|
+
focusableSelectors
|
|
82
|
+
))
|
|
83
|
+
const firstFocusableModalChild = focusableModalChildren.find(
|
|
84
|
+
el => !!el.getBoundingClientRect().width
|
|
85
|
+
)
|
|
86
|
+
const reversedModalChildren = [ ...focusableModalChildren ].reverse()
|
|
87
|
+
const lastFocusableModalChild = reversedModalChildren.find(
|
|
88
|
+
el => !!el.getBoundingClientRect().width
|
|
89
|
+
)
|
|
90
|
+
if (firstFocusableModalChild) {
|
|
91
|
+
const relatedTarget = (event as FocusEvent).relatedTarget // The element last focused
|
|
92
|
+
if (relatedTarget === firstFocusableModalChild && lastFocusableModalChild) {
|
|
93
|
+
// If focus moves from first element -> outside modal, focus the last element instead
|
|
94
|
+
lastFocusableModalChild.focus()
|
|
95
|
+
} else if (relatedTarget === lastFocusableModalChild && firstFocusableModalChild) {
|
|
96
|
+
// If focus moves from last element -> outside modal, focus the first element instead
|
|
97
|
+
firstFocusableModalChild.focus()
|
|
98
|
+
} else if (relatedTarget && relatedTarget instanceof HTMLElement) {
|
|
99
|
+
// If focus goes outside in a different way, return focus to where it came from if possible
|
|
100
|
+
relatedTarget.focus()
|
|
101
|
+
} else {
|
|
102
|
+
// Otherwise, just return focus to the first element
|
|
103
|
+
firstFocusableModalChild.focus()
|
|
104
|
+
}
|
|
97
105
|
} else {
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (modalContainerEl.tabIndex === -1) {
|
|
104
|
-
modalContainerEl.tabIndex = 0
|
|
106
|
+
// If the modal doesn't have any focusable children, focus the container instead
|
|
107
|
+
if (modalContainerEl.tabIndex === -1) {
|
|
108
|
+
modalContainerEl.tabIndex = 0
|
|
109
|
+
}
|
|
110
|
+
modalContainerEl.focus()
|
|
105
111
|
}
|
|
106
|
-
modalContainerEl.focus()
|
|
107
112
|
}
|
|
108
113
|
}
|
|
114
|
+
document.addEventListener('focusin', this.focusListener)
|
|
109
115
|
}
|
|
110
|
-
document.addEventListener('focusin', this.focusListener)
|
|
111
116
|
|
|
112
117
|
// prevent scrolling outside of modal
|
|
113
118
|
const openModalCount = Number(document.body.getAttribute('data-openModalCount'))
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
@click="emitClose"
|
|
19
19
|
/>
|
|
20
20
|
</slot>
|
|
21
|
+
<div
|
|
22
|
+
class="uikit-question__tag-mobile"
|
|
23
|
+
v-breakpoint:questionEl="breakpoints"
|
|
24
|
+
>
|
|
25
|
+
<slot name="tagMobile" />
|
|
26
|
+
</div>
|
|
21
27
|
<div
|
|
22
28
|
v-breakpoint:questionEl="breakpoints"
|
|
23
29
|
class="uikit-question__main"
|
|
@@ -80,6 +86,12 @@
|
|
|
80
86
|
</div>
|
|
81
87
|
</h2>
|
|
82
88
|
</slot>
|
|
89
|
+
<div
|
|
90
|
+
class="uikit-question__tag"
|
|
91
|
+
v-breakpoint:questionEl="breakpoints"
|
|
92
|
+
>
|
|
93
|
+
<slot name="tag" />
|
|
94
|
+
</div>
|
|
83
95
|
</div>
|
|
84
96
|
</slot>
|
|
85
97
|
<div
|
|
@@ -564,6 +576,23 @@
|
|
|
564
576
|
/>
|
|
565
577
|
</div>
|
|
566
578
|
</template>
|
|
579
|
+
<div
|
|
580
|
+
v-breakpoint:questionEl="breakpoints"
|
|
581
|
+
class="uikit-question__motivational-moment"
|
|
582
|
+
:class="{
|
|
583
|
+
'uikit-question__motivational-moment--mcr': isMCR,
|
|
584
|
+
'uikit-question__motivational-moment--explanation': showExplanation,
|
|
585
|
+
'uikit-question__motivational-moment--passage-and-image': showPassageAndImage,
|
|
586
|
+
}"
|
|
587
|
+
>
|
|
588
|
+
<slot
|
|
589
|
+
name="motivationalMoment"
|
|
590
|
+
:showAnswers="showAnswers"
|
|
591
|
+
:answerKeys="answerKeys"
|
|
592
|
+
:choiceKey="choice.key"
|
|
593
|
+
:isCorrect="isCorrect"
|
|
594
|
+
/>
|
|
595
|
+
</div>
|
|
567
596
|
</div>
|
|
568
597
|
<slot
|
|
569
598
|
name="showNamesTable"
|
|
@@ -704,6 +733,21 @@
|
|
|
704
733
|
</div>
|
|
705
734
|
</div>
|
|
706
735
|
</slot>
|
|
736
|
+
<div
|
|
737
|
+
v-breakpoint:questionEl="breakpoints"
|
|
738
|
+
class="uikit-question__motivational-moment-bottom"
|
|
739
|
+
:class="{
|
|
740
|
+
'uikit-question__motivational-moment-bottom--mcr': isMCR,
|
|
741
|
+
'uikit-question__motivational-moment-bottom--explanation': showExplanation,
|
|
742
|
+
'uikit-question__motivational-moment-bottom--passage-and-image': showPassageAndImage,
|
|
743
|
+
}"
|
|
744
|
+
>
|
|
745
|
+
<slot
|
|
746
|
+
name="motivationalMomentBottom"
|
|
747
|
+
:showAnswers="showAnswers"
|
|
748
|
+
:isCorrect="isCorrect"
|
|
749
|
+
/>
|
|
750
|
+
</div>
|
|
707
751
|
<div
|
|
708
752
|
v-if="!reviewMode"
|
|
709
753
|
v-breakpoint:questionEl="breakpoints"
|
|
@@ -2106,6 +2150,26 @@ export default class Question extends Vue {
|
|
|
2106
2150
|
font-weight: 500;
|
|
2107
2151
|
}
|
|
2108
2152
|
|
|
2153
|
+
&__tag {
|
|
2154
|
+
margin-top: 8px;
|
|
2155
|
+
margin-bottom: -5px;
|
|
2156
|
+
|
|
2157
|
+
&--mobile {
|
|
2158
|
+
display: none;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
&__tag-mobile {
|
|
2163
|
+
display: none;
|
|
2164
|
+
position: absolute;
|
|
2165
|
+
top: 14px;
|
|
2166
|
+
right: 14px;
|
|
2167
|
+
|
|
2168
|
+
&--mobile {
|
|
2169
|
+
display: block;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2109
2173
|
&__prompt {
|
|
2110
2174
|
outline: none;
|
|
2111
2175
|
font-weight: 600;
|
|
@@ -3158,6 +3222,44 @@ export default class Question extends Vue {
|
|
|
3158
3222
|
}
|
|
3159
3223
|
}
|
|
3160
3224
|
|
|
3225
|
+
&__motivational-moment {
|
|
3226
|
+
position: absolute;
|
|
3227
|
+
right: -150px;
|
|
3228
|
+
top: 20px;
|
|
3229
|
+
|
|
3230
|
+
&--mcr {
|
|
3231
|
+
display: none;
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
&--passage-and-image {
|
|
3235
|
+
display: none;
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
&--explanation {
|
|
3239
|
+
display: none;
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
&--tablet-portrait {
|
|
3243
|
+
display: none;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
&__motivational-moment-bottom {
|
|
3248
|
+
display: none;
|
|
3249
|
+
|
|
3250
|
+
&--mcr {
|
|
3251
|
+
display: block;
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
&--passage-and-image:not(#{&}--tablet-portrait) {
|
|
3255
|
+
display: block;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
&--tablet-portrait {
|
|
3259
|
+
display: block;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3161
3263
|
&__summary {
|
|
3162
3264
|
position: relative;
|
|
3163
3265
|
background-color: $white;
|