@pocketprep/ui-kit 3.4.33 → 3.4.35
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 +5351 -5234
- package/dist/@pocketprep/ui-kit.js.map +1 -1
- package/dist/@pocketprep/ui-kit.umd.cjs +7 -7
- package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/lib/components/Forms/Textarea.vue +72 -10
- package/lib/components/Quiz/Question/ChoicesContainer.vue +8 -0
- package/lib/components/Quiz/Question/DropdownExplanation.vue +12 -1
- package/lib/components/Quiz/Question/Explanation.vue +12 -1
- package/lib/components/Quiz/Question.vue +41 -3
- package/lib/components/Tooltips/Tooltip.vue +18 -3
- package/lib/utils.ts +22 -0
- package/package.json +1 -1
|
@@ -4,22 +4,49 @@
|
|
|
4
4
|
@mouseover="hover = true"
|
|
5
5
|
@mouseout="hover = false"
|
|
6
6
|
>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
<div
|
|
8
|
+
class="uikit-textarea__container"
|
|
9
|
+
>
|
|
10
|
+
<label
|
|
11
|
+
v-if="label"
|
|
12
|
+
v-dark="isDarkMode"
|
|
13
|
+
class="uikit-textarea__label"
|
|
14
|
+
:class="{
|
|
15
|
+
'uikit-textarea__label--hover': hover,
|
|
16
|
+
'uikit-textarea__label--focus': focus
|
|
17
|
+
}"
|
|
18
|
+
>{{ label }}</label>
|
|
19
|
+
<div
|
|
20
|
+
v-if="showIconAndTooltip"
|
|
21
|
+
@mouseenter="showTextAreaTooltip = true"
|
|
22
|
+
@mouseleave="showTextAreaTooltip = false"
|
|
23
|
+
@focus="showTextAreaTooltip = true"
|
|
24
|
+
@blur="showTextAreaTooltip = false"
|
|
25
|
+
>
|
|
26
|
+
<Tooltip
|
|
27
|
+
v-if="showTextAreaTooltip"
|
|
28
|
+
:key="tooltipText"
|
|
29
|
+
theme="offset"
|
|
30
|
+
:is-dark-mode="isDarkMode"
|
|
31
|
+
>
|
|
32
|
+
{{ tooltipText }}
|
|
33
|
+
</Tooltip>
|
|
34
|
+
<Icon
|
|
35
|
+
type="info"
|
|
36
|
+
class="uikit-textarea__icon"
|
|
37
|
+
:class="{
|
|
38
|
+
'uikit-textarea__icon--dark': isDarkMode
|
|
39
|
+
}"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
16
43
|
<textarea
|
|
17
44
|
v-dark="isDarkMode"
|
|
18
45
|
:value="modelValue"
|
|
19
46
|
:disabled="disabled"
|
|
20
47
|
:placeholder="placeholder"
|
|
21
48
|
:autofocus="autoFocus"
|
|
22
|
-
:style="{ height }"
|
|
49
|
+
:style="{ height, width }"
|
|
23
50
|
class="uikit-textarea__textarea"
|
|
24
51
|
:class="{
|
|
25
52
|
'uikit-textarea__textarea--hover': hover,
|
|
@@ -34,9 +61,15 @@
|
|
|
34
61
|
|
|
35
62
|
<script lang="ts">
|
|
36
63
|
import { Component, Vue, Prop, Emit } from 'vue-facing-decorator'
|
|
64
|
+
import Icon from '../Icons/Icon.vue'
|
|
65
|
+
import Tooltip from '../Tooltips/Tooltip.vue'
|
|
37
66
|
import { dark } from '../../directives'
|
|
38
67
|
|
|
39
68
|
@Component({
|
|
69
|
+
components: {
|
|
70
|
+
Icon,
|
|
71
|
+
Tooltip,
|
|
72
|
+
},
|
|
40
73
|
directives: {
|
|
41
74
|
dark,
|
|
42
75
|
},
|
|
@@ -49,10 +82,14 @@ export default class Textarea extends Vue {
|
|
|
49
82
|
@Prop() disabled?: boolean
|
|
50
83
|
@Prop() autoFocus?: boolean
|
|
51
84
|
@Prop({ default: '150px' }) height?: string
|
|
85
|
+
@Prop({ default: '341px' }) width?: string
|
|
52
86
|
@Prop({ default: false }) isDarkMode!: boolean
|
|
87
|
+
@Prop({ default: false }) showIconAndTooltip!: boolean
|
|
88
|
+
@Prop({ default: '' }) tooltipText!: string
|
|
53
89
|
|
|
54
90
|
hover = false
|
|
55
91
|
focus = false
|
|
92
|
+
showTextAreaTooltip = false
|
|
56
93
|
|
|
57
94
|
@Emit('update:modelValue')
|
|
58
95
|
valueChange ($event: Event) {
|
|
@@ -66,6 +103,12 @@ export default class Textarea extends Vue {
|
|
|
66
103
|
@import '../../styles/breakpoints';
|
|
67
104
|
|
|
68
105
|
.uikit-textarea {
|
|
106
|
+
&__container {
|
|
107
|
+
position: relative;
|
|
108
|
+
display: flex;
|
|
109
|
+
outline: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
69
112
|
&__label {
|
|
70
113
|
font-size: 13px;
|
|
71
114
|
font-weight: 600;
|
|
@@ -99,6 +142,25 @@ export default class Textarea extends Vue {
|
|
|
99
142
|
}
|
|
100
143
|
}
|
|
101
144
|
|
|
145
|
+
.uikit-tooltip {
|
|
146
|
+
position: absolute;
|
|
147
|
+
left: 50%;
|
|
148
|
+
top: calc(-100% - 14px);
|
|
149
|
+
|
|
150
|
+
@include breakpoint('black-bear') {
|
|
151
|
+
left: 35%;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
&__icon {
|
|
156
|
+
padding: 0 0 2px 5px;
|
|
157
|
+
color: $brand-blue;
|
|
158
|
+
|
|
159
|
+
&--dark {
|
|
160
|
+
color: $banana-bread;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
102
164
|
&__textarea {
|
|
103
165
|
background-color: $white;
|
|
104
166
|
border: 1px solid rgba($pewter, 0.85);
|
|
@@ -166,7 +166,9 @@
|
|
|
166
166
|
:is-dark-mode="isDarkMode"
|
|
167
167
|
:question-el="questionEl"
|
|
168
168
|
:breakpoints="breakpoints"
|
|
169
|
+
:keyword-definitions="keywordDefinitions"
|
|
169
170
|
@toggleDropdownExplanationImageLongAlt="toggleDropdownExplanationImageLongAlt"
|
|
171
|
+
@click="handleClick"
|
|
170
172
|
/>
|
|
171
173
|
</div>
|
|
172
174
|
<template v-if="!globalMetrics">
|
|
@@ -323,6 +325,7 @@ export default class ChoicesContainer extends Vue {
|
|
|
323
325
|
'tablet-portrait': 1023,
|
|
324
326
|
'tablet-landscape': 1439,
|
|
325
327
|
} }) breakpoints!: TBreakPointsObject
|
|
328
|
+
@Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
|
|
326
329
|
|
|
327
330
|
stripText (string?: string) {
|
|
328
331
|
return string?.replace(/<[^\s>]+[^>]*>/gi, ' ').trim()
|
|
@@ -383,6 +386,11 @@ export default class ChoicesContainer extends Vue {
|
|
|
383
386
|
toggleDropdownExplanationImageLongAlt () {
|
|
384
387
|
return
|
|
385
388
|
}
|
|
389
|
+
|
|
390
|
+
@Emit('click')
|
|
391
|
+
handleClick (event: MouseEvent) {
|
|
392
|
+
return event
|
|
393
|
+
}
|
|
386
394
|
}
|
|
387
395
|
</script>
|
|
388
396
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<div
|
|
11
11
|
v-dark="isDarkMode"
|
|
12
12
|
class="uikit-question-dropdown-explanation__dropdown-explanation-text"
|
|
13
|
-
v-html="
|
|
13
|
+
v-html="explanation"
|
|
14
14
|
/>
|
|
15
15
|
<img
|
|
16
16
|
v-if="explanationImageUrl"
|
|
@@ -74,6 +74,7 @@ import Icon from '../../Icons/Icon.vue'
|
|
|
74
74
|
import PocketButton from '../../Buttons/Button.vue'
|
|
75
75
|
import { breakpoint, dark } from '../../../directives'
|
|
76
76
|
import type { TBreakPointsObject, TChoice, TChoiceKey } from './../question'
|
|
77
|
+
import { highlightKeywordsInText } from '../../../utils'
|
|
77
78
|
|
|
78
79
|
@Component({
|
|
79
80
|
components: {
|
|
@@ -105,6 +106,16 @@ export default class DropdownExplanation extends Vue {
|
|
|
105
106
|
'tablet-portrait': 1023,
|
|
106
107
|
'tablet-landscape': 1439,
|
|
107
108
|
} }) breakpoints!: TBreakPointsObject
|
|
109
|
+
@Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
|
|
110
|
+
|
|
111
|
+
get explanation () {
|
|
112
|
+
return highlightKeywordsInText({
|
|
113
|
+
text: this.question.explanation || '',
|
|
114
|
+
keywordDefinitions: this.keywordDefinitions,
|
|
115
|
+
isDarkMode: this.isDarkMode,
|
|
116
|
+
location: 'dropdown-explanation',
|
|
117
|
+
})
|
|
118
|
+
}
|
|
108
119
|
|
|
109
120
|
@Emit('toggleDropdownExplanationImageLongAlt')
|
|
110
121
|
toggleDropdownExplanationImageLongAlt () {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
v-dark="isDarkMode"
|
|
18
18
|
v-breakpoint:questionEl="breakpoints"
|
|
19
19
|
class="uikit-question-explanation__explanation-text"
|
|
20
|
-
v-html="
|
|
20
|
+
v-html="explanation"
|
|
21
21
|
/>
|
|
22
22
|
<img
|
|
23
23
|
v-if="explanationImageUrl"
|
|
@@ -92,6 +92,7 @@ import Icon from '../../Icons/Icon.vue'
|
|
|
92
92
|
import PocketButton from '../../Buttons/Button.vue'
|
|
93
93
|
import { breakpoint, dark } from '../../../directives'
|
|
94
94
|
import type { TBreakPointsObject } from './../question'
|
|
95
|
+
import { highlightKeywordsInText } from '../../../utils'
|
|
95
96
|
|
|
96
97
|
@Component({
|
|
97
98
|
components: {
|
|
@@ -121,6 +122,16 @@ export default class Explanation extends Vue {
|
|
|
121
122
|
'tablet-portrait': 1023,
|
|
122
123
|
'tablet-landscape': 1439,
|
|
123
124
|
} }) breakpoints!: TBreakPointsObject
|
|
125
|
+
@Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
|
|
126
|
+
|
|
127
|
+
get explanation () {
|
|
128
|
+
return highlightKeywordsInText({
|
|
129
|
+
text: this.question.explanation || '',
|
|
130
|
+
keywordDefinitions: this.keywordDefinitions,
|
|
131
|
+
isDarkMode: this.isDarkMode,
|
|
132
|
+
location: 'explanation',
|
|
133
|
+
})
|
|
134
|
+
}
|
|
124
135
|
|
|
125
136
|
@Emit('toggleExplanationImageLongAlt')
|
|
126
137
|
toggleExplanationImageLongAlt () {
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
:class="{
|
|
67
67
|
'uikit-question__prompt--passage-and-image': question.passage || passageImageUrl,
|
|
68
68
|
}"
|
|
69
|
-
|
|
69
|
+
@click="keywordClick"
|
|
70
|
+
v-html="prompt"
|
|
70
71
|
/>
|
|
71
72
|
<PocketButton
|
|
72
73
|
v-if="question.passage || passageImageUrl"
|
|
@@ -178,6 +179,7 @@
|
|
|
178
179
|
:is-dark-mode="isDarkMode"
|
|
179
180
|
:question-el="questionEl"
|
|
180
181
|
:breakpoints="breakpoints"
|
|
182
|
+
:keyword-definitions="keywordDefinitions"
|
|
181
183
|
@emitChoiceMouseOver="choiceMouseOver"
|
|
182
184
|
@emitChoiceMouseLeave="choiceMouseLeave"
|
|
183
185
|
@emitChoiceFocusIn="choiceFocusIn"
|
|
@@ -189,6 +191,7 @@
|
|
|
189
191
|
@emitClickChoiceStrike="clickChoiceStrike"
|
|
190
192
|
@toggleChoiceExplanation="toggleExplanation"
|
|
191
193
|
@toggleDropdownExplanationImageLongAlt="toggleExplanationImageLongAlt"
|
|
194
|
+
@click="keywordClick"
|
|
192
195
|
>
|
|
193
196
|
<template #motivationalMoment="{
|
|
194
197
|
isCorrect,
|
|
@@ -330,7 +333,7 @@
|
|
|
330
333
|
:breakpoints="breakpoints"
|
|
331
334
|
@emitTogglePassageImageLongAlt="togglePassageImageLongAlt"
|
|
332
335
|
@emitMoveFocusToPrompt="moveFocusToPrompt"
|
|
333
|
-
/>
|
|
336
|
+
/>
|
|
334
337
|
<Explanation
|
|
335
338
|
ref="uikit-question__explanation"
|
|
336
339
|
class="uikit-question__explanation"
|
|
@@ -347,8 +350,10 @@
|
|
|
347
350
|
:is-dark-mode="isDarkMode"
|
|
348
351
|
:question-el="questionEl"
|
|
349
352
|
:breakpoints="breakpoints"
|
|
353
|
+
:keyword-definitions="keywordDefinitions"
|
|
350
354
|
@toggleExplanationImageLongAlt="toggleExplanationImageLongAlt"
|
|
351
355
|
@toggleExplanation="toggleExplanation"
|
|
356
|
+
@click="keywordClick"
|
|
352
357
|
/>
|
|
353
358
|
</div>
|
|
354
359
|
</div>
|
|
@@ -373,7 +378,7 @@ import Explanation from '../Quiz/Question/Explanation.vue'
|
|
|
373
378
|
import PassageAndImage from '../Quiz/Question/PassageAndImage.vue'
|
|
374
379
|
import type { Study } from '@pocketprep/types'
|
|
375
380
|
import { breakpoint, dark } from '../../directives'
|
|
376
|
-
import { studyModes } from '../../utils'
|
|
381
|
+
import { highlightKeywordsInText, studyModes } from '../../utils'
|
|
377
382
|
import type { Ref, TQuizMode, TChoiceKey, TChoice, TChoiceScores, TNamesRow, TViewNames } from './question'
|
|
378
383
|
|
|
379
384
|
@Component({
|
|
@@ -421,6 +426,7 @@ export default class Question extends Vue {
|
|
|
421
426
|
@Prop({ default: false }) showPaywall!: boolean
|
|
422
427
|
@Prop({ default: false }) hideReferences!: boolean
|
|
423
428
|
@Prop({ default: false }) isTeachReview!: boolean
|
|
429
|
+
@Prop({ default: [] }) keywordDefinitions!: { keyword: string; definition: string }[]
|
|
424
430
|
|
|
425
431
|
hoverChoiceKey: TChoiceKey | null = null
|
|
426
432
|
focusChoiceKey: TChoiceKey | null = null
|
|
@@ -606,6 +612,15 @@ export default class Question extends Vue {
|
|
|
606
612
|
return this.selectedChoices.length === 0
|
|
607
613
|
}
|
|
608
614
|
|
|
615
|
+
get prompt () {
|
|
616
|
+
return highlightKeywordsInText({
|
|
617
|
+
text: this.question.prompt,
|
|
618
|
+
keywordDefinitions: this.keywordDefinitions,
|
|
619
|
+
isDarkMode: this.isDarkMode,
|
|
620
|
+
location: 'prompt',
|
|
621
|
+
})
|
|
622
|
+
}
|
|
623
|
+
|
|
609
624
|
mounted () {
|
|
610
625
|
if (this.reviewMode) {
|
|
611
626
|
this.startReviewMode()
|
|
@@ -718,6 +733,19 @@ export default class Question extends Vue {
|
|
|
718
733
|
}, 0)
|
|
719
734
|
}
|
|
720
735
|
|
|
736
|
+
@Emit('keyword-click')
|
|
737
|
+
keywordClick (event: MouseEvent) {
|
|
738
|
+
const target = event.target as HTMLElement
|
|
739
|
+
if (target.classList.contains('keyword-highlight')) {
|
|
740
|
+
const keyword = target.innerHTML
|
|
741
|
+
const location = target.getAttribute('data-location')
|
|
742
|
+
return {
|
|
743
|
+
keyword,
|
|
744
|
+
location,
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
721
749
|
startReviewMode () {
|
|
722
750
|
this.showAnswers = true
|
|
723
751
|
this.showExplanation = this.defaultShowExplanation === null ? true : this.defaultShowExplanation
|
|
@@ -1283,6 +1311,16 @@ export default class Question extends Vue {
|
|
|
1283
1311
|
}
|
|
1284
1312
|
}
|
|
1285
1313
|
|
|
1314
|
+
.keyword-highlight {
|
|
1315
|
+
text-decoration: underline;
|
|
1316
|
+
text-decoration-color: $brand-blue;
|
|
1317
|
+
cursor: pointer;
|
|
1318
|
+
|
|
1319
|
+
&--dark {
|
|
1320
|
+
text-decoration-color: $banana-bread;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1286
1324
|
&__prompt {
|
|
1287
1325
|
outline: none;
|
|
1288
1326
|
font-weight: 600;
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
class="uikit-tooltip-popup__triangle"
|
|
18
18
|
:class="{
|
|
19
19
|
'uikit-tooltip-popup__triangle--rightalign': theme === 'rightalign',
|
|
20
|
-
'uikit-tooltip-popup__triangle--leftalign': theme === 'leftalign'
|
|
20
|
+
'uikit-tooltip-popup__triangle--leftalign': theme === 'leftalign',
|
|
21
|
+
'uikit-tooltip-popup__triangle--offset': theme === 'offset'
|
|
21
22
|
}"
|
|
22
23
|
/>
|
|
23
24
|
<span>
|
|
@@ -37,7 +38,7 @@ import { dark } from '../../directives'
|
|
|
37
38
|
},
|
|
38
39
|
})
|
|
39
40
|
export default class Tooltip extends Vue {
|
|
40
|
-
@Prop({ default: '' }) theme!: 'leftalign' | 'rightalign'
|
|
41
|
+
@Prop({ default: '' }) theme!: 'leftalign' | 'rightalign' | 'offset'
|
|
41
42
|
@Prop({ default: null }) styles!: null | Record<string, string>
|
|
42
43
|
@Prop({ default: false }) isDarkMode!: boolean
|
|
43
44
|
|
|
@@ -128,8 +129,12 @@ export default class Tooltip extends Vue {
|
|
|
128
129
|
span {
|
|
129
130
|
position: relative;
|
|
130
131
|
z-index: 1;
|
|
131
|
-
max-width:
|
|
132
|
+
max-width: 482px;
|
|
132
133
|
display: block;
|
|
134
|
+
|
|
135
|
+
@include breakpoint('black-bear') {
|
|
136
|
+
max-width: 341px;
|
|
137
|
+
}
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
&__triangle {
|
|
@@ -152,6 +157,16 @@ export default class Tooltip extends Vue {
|
|
|
152
157
|
transform: none;
|
|
153
158
|
}
|
|
154
159
|
|
|
160
|
+
&--offset {
|
|
161
|
+
left: 42%;
|
|
162
|
+
transform: none;
|
|
163
|
+
|
|
164
|
+
@include breakpoint('black-bear') {
|
|
165
|
+
left: 60%;
|
|
166
|
+
transform: none;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
155
170
|
&--dark {
|
|
156
171
|
border-top-color: $white;
|
|
157
172
|
}
|
package/lib/utils.ts
CHANGED
|
@@ -58,3 +58,25 @@ export const studyModes = {
|
|
|
58
58
|
iconColorDM: BrandColors.pewter,
|
|
59
59
|
},
|
|
60
60
|
} as const
|
|
61
|
+
|
|
62
|
+
export const highlightKeywordsInText = (params: {
|
|
63
|
+
text: string
|
|
64
|
+
keywordDefinitions: { keyword: string }[]
|
|
65
|
+
isDarkMode: boolean
|
|
66
|
+
location?: string
|
|
67
|
+
}) => {
|
|
68
|
+
if (!params.keywordDefinitions.length) {
|
|
69
|
+
return params.text
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const keywords = params.keywordDefinitions.map(k => k.keyword)
|
|
73
|
+
|
|
74
|
+
return keywords.reduce((acc, word) => {
|
|
75
|
+
const regex = new RegExp(`(${word})`, 'i')
|
|
76
|
+
return acc.replace(
|
|
77
|
+
regex,
|
|
78
|
+
`<span class="keyword-highlight${params.isDarkMode
|
|
79
|
+
? ' keyword-highlight--dark' : ''}" data-location="${params.location}">$1</span>`
|
|
80
|
+
)
|
|
81
|
+
}, params.text)
|
|
82
|
+
}
|