@pocketprep/ui-kit 3.8.4 → 3.9.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.
- package/dist/@pocketprep/ui-kit.css +1 -1
- package/dist/@pocketprep/ui-kit.js +14469 -17731
- package/dist/@pocketprep/ui-kit.js.map +1 -1
- package/dist/@pocketprep/ui-kit.umd.cjs +19 -29
- package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
- package/eslint.config.ts +38 -11
- package/lib/SVGDefinitions.vue +32 -35
- package/lib/components/Banners/Banner.vue +10 -14
- package/lib/components/Blobs/Blob.vue +6 -14
- package/lib/components/Blobs/BlobEmptyState.vue +9 -8
- package/lib/components/Blobs/blob.d.ts +1 -1
- package/lib/components/BundleIcons/BundleIcon.vue +36 -63
- package/lib/components/BundleIcons/bundleIcon.d.ts +1 -1
- package/lib/components/Bundles/BundleList.vue +71 -59
- package/lib/components/Bundles/BundleSearch.vue +93 -117
- package/lib/components/Bundles/PremiumPill.vue +6 -12
- package/lib/components/Buttons/Button.vue +32 -35
- package/lib/components/Buttons/Link.vue +32 -31
- package/lib/components/Buttons/Tab.vue +14 -17
- package/lib/components/Calendar/Calendar.vue +87 -85
- package/lib/components/Charts/Bar.vue +192 -263
- package/lib/components/Charts/Pie.vue +55 -61
- package/lib/components/Charts/highcharts-wrap.ts +81 -0
- package/lib/components/Controls/SegmentControl.vue +26 -24
- package/lib/components/Controls/Slider.vue +51 -47
- package/lib/components/Controls/ToggleSwitch.vue +33 -31
- package/lib/components/EmptyStates/EmptyState.vue +69 -73
- package/lib/components/Exams/ExamCard.vue +59 -47
- package/lib/components/Exams/ExamMenuCard.vue +30 -28
- package/lib/components/Filters/FilterDropdown.vue +83 -86
- package/lib/components/Filters/FilterOptions.vue +83 -88
- package/lib/components/Forms/Checkbox.vue +27 -27
- package/lib/components/Forms/CheckboxOption.vue +30 -30
- package/lib/components/Forms/Errors.vue +21 -24
- package/lib/components/Forms/Input.vue +71 -59
- package/lib/components/Forms/Radio.vue +2 -2
- package/lib/components/Forms/RadioButton.vue +8 -8
- package/lib/components/Forms/Select.vue +265 -257
- package/lib/components/Forms/Textarea.vue +49 -35
- package/lib/components/Icons/IconAccordionArrow.vue +7 -9
- package/lib/components/Icons/IconActivity.vue +7 -9
- package/lib/components/Icons/IconAdd.vue +7 -11
- package/lib/components/Icons/IconAddCircle.vue +7 -9
- package/lib/components/Icons/IconArrow.vue +7 -9
- package/lib/components/Icons/IconBarChart.vue +7 -9
- package/lib/components/Icons/IconCalendar.vue +7 -9
- package/lib/components/Icons/IconCalendarPicker.vue +7 -9
- package/lib/components/Icons/IconChat.vue +7 -9
- package/lib/components/Icons/IconCheck.vue +7 -9
- package/lib/components/Icons/IconClose.vue +7 -9
- package/lib/components/Icons/IconConcept.vue +1 -1
- package/lib/components/Icons/IconCorrect.vue +7 -9
- package/lib/components/Icons/IconEdit.vue +7 -11
- package/lib/components/Icons/IconExam.vue +7 -9
- package/lib/components/Icons/IconExternalLink.vue +7 -9
- package/lib/components/Icons/IconEyeHide.vue +7 -9
- package/lib/components/Icons/IconEyeShow.vue +7 -9
- package/lib/components/Icons/IconFilter.vue +7 -9
- package/lib/components/Icons/IconFilterActive.vue +7 -9
- package/lib/components/Icons/IconFlag.vue +7 -9
- package/lib/components/Icons/IconFlagContent.vue +8 -9
- package/lib/components/Icons/IconFlagFeedback.vue +8 -10
- package/lib/components/Icons/IconFlagFilled.vue +7 -9
- package/lib/components/Icons/IconFullView.vue +7 -9
- package/lib/components/Icons/IconFullViewActive.vue +7 -9
- package/lib/components/Icons/IconGridDrag.vue +2 -2
- package/lib/components/Icons/IconHandle.vue +7 -9
- package/lib/components/Icons/IconHeart.vue +7 -9
- package/lib/components/Icons/IconHelp.vue +7 -9
- package/lib/components/Icons/IconHighlight.vue +2 -2
- package/lib/components/Icons/IconHourglass.vue +7 -9
- package/lib/components/Icons/IconImage.vue +7 -9
- package/lib/components/Icons/IconIncorrect.vue +7 -9
- package/lib/components/Icons/IconInfo.vue +7 -9
- package/lib/components/Icons/IconKeyboard.vue +7 -9
- package/lib/components/Icons/IconLaunch.vue +7 -9
- package/lib/components/Icons/IconLevelUp.vue +7 -9
- package/lib/components/Icons/IconLightbulb.vue +7 -9
- package/lib/components/Icons/IconLightning.vue +7 -9
- package/lib/components/Icons/IconLink.vue +7 -9
- package/lib/components/Icons/IconList.vue +7 -9
- package/lib/components/Icons/IconLoading.vue +7 -9
- package/lib/components/Icons/IconLoading2.vue +11 -11
- package/lib/components/Icons/IconLock.vue +7 -9
- package/lib/components/Icons/IconMissedQuestions.vue +7 -9
- package/lib/components/Icons/IconMoon.vue +7 -9
- package/lib/components/Icons/IconPaginationArrow.vue +7 -9
- package/lib/components/Icons/IconPaginationArrowDouble.vue +7 -9
- package/lib/components/Icons/IconPassage.vue +7 -9
- package/lib/components/Icons/IconPencil.vue +7 -9
- package/lib/components/Icons/IconPeople.vue +7 -9
- package/lib/components/Icons/IconPercent.vue +7 -9
- package/lib/components/Icons/IconPerson.vue +8 -9
- package/lib/components/Icons/IconPresent.vue +7 -9
- package/lib/components/Icons/IconPreview.vue +7 -9
- package/lib/components/Icons/IconQuestions.vue +7 -9
- package/lib/components/Icons/IconQuick10.vue +7 -9
- package/lib/components/Icons/IconQuickActions.vue +2 -2
- package/lib/components/Icons/IconRecommendedFilter.vue +1 -1
- package/lib/components/Icons/IconRemoveCircle.vue +7 -9
- package/lib/components/Icons/IconReviewFlag.vue +7 -9
- package/lib/components/Icons/IconSearch.vue +7 -9
- package/lib/components/Icons/IconShare.vue +7 -9
- package/lib/components/Icons/IconSideBar.vue +7 -9
- package/lib/components/Icons/IconSideBarActive.vue +7 -9
- package/lib/components/Icons/IconStar.vue +1 -1
- package/lib/components/Icons/IconStopwatch.vue +7 -9
- package/lib/components/Icons/IconStrike.vue +7 -9
- package/lib/components/Icons/IconSubject.vue +7 -9
- package/lib/components/Icons/IconText.vue +7 -9
- package/lib/components/Icons/IconTimer.vue +8 -9
- package/lib/components/Icons/IconWarning.vue +7 -9
- package/lib/components/Icons/icon.d.ts +1 -1
- package/lib/components/Loaders/SkeletonLoader.vue +1 -5
- package/lib/components/Modal/Modal.vue +23 -29
- package/lib/components/Modal/ModalContainer.vue +135 -133
- package/lib/components/Onboarding/EmailAuth.vue +66 -70
- package/lib/components/Onboarding/MagicCodeEntry.vue +88 -83
- package/lib/components/Pagination/QuestionReviewPagination.vue +3 -3
- package/lib/components/Pagination/TablePagination.vue +47 -44
- package/lib/components/PhonePerson/PhonePerson.vue +18 -18
- package/lib/components/PhonePerson/phonePerson.d.ts +1 -1
- package/lib/components/Quiz/FlagToggle.vue +45 -44
- package/lib/components/Quiz/GlobalMetricsToggle.vue +29 -28
- package/lib/components/Quiz/KeyboardShortcutsButton.vue +16 -23
- package/lib/components/Quiz/KeyboardShortcutsModal.vue +36 -37
- package/lib/components/Quiz/Question/BuildListChoicesContainer.vue +65 -65
- package/lib/components/Quiz/Question/ChoicesContainer.vue +5 -5
- package/lib/components/Quiz/Question/DropdownExplanation.vue +5 -5
- package/lib/components/Quiz/Question/Explanation.vue +6 -6
- package/lib/components/Quiz/Question/MPMCChoicesContainer.vue +17 -17
- package/lib/components/Quiz/Question/MPMCRadioGroup.vue +2 -2
- package/lib/components/Quiz/Question/MatrixChoicesContainer.vue +39 -39
- package/lib/components/Quiz/Question/MatrixRadioGroup.vue +6 -6
- package/lib/components/Quiz/Question/MobileMatrixChoicesContainer.vue +27 -28
- package/lib/components/Quiz/Question/MobileMatrixRadioGroup.vue +2 -2
- package/lib/components/Quiz/Question/PassageAndImage.vue +3 -3
- package/lib/components/Quiz/Question/PassageAndImageDropdown.vue +7 -7
- package/lib/components/Quiz/Question/Paywall.vue +2 -2
- package/lib/components/Quiz/Question/QuestionContext.vue +1 -1
- package/lib/components/Quiz/Question/StatsSummary.vue +2 -2
- package/lib/components/Quiz/Question/Summary.vue +11 -11
- package/lib/components/Quiz/Question.vue +90 -82
- package/lib/components/Quiz/QuizContainer.vue +1 -1
- package/lib/components/Quiz/QuizProgressBar.vue +23 -23
- package/lib/components/Quiz/question.d.ts +3 -3
- package/lib/components/Search/Pill.vue +16 -19
- package/lib/components/Search/Search.vue +52 -47
- package/lib/components/SidePanels/SidePanel.vue +168 -174
- package/lib/components/Tables/Table.vue +135 -122
- package/lib/components/Tables/TableActions.vue +81 -76
- package/lib/components/Tables/table.d.ts +1 -1
- package/lib/components/Tags/Tag.vue +49 -39
- package/lib/components/Toasts/Toast.vue +44 -42
- package/lib/components/Tooltips/OverflowTooltip.vue +39 -45
- package/lib/components/Tooltips/Tooltip.vue +69 -70
- package/lib/directives.ts +4 -4
- package/lib/utils.ts +13 -12
- package/package.json +27 -28
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
}"
|
|
17
17
|
>{{ label }}</label>
|
|
18
18
|
<div
|
|
19
|
-
ref="uikit-select__input-container"
|
|
19
|
+
ref="uikit-select__input-container"
|
|
20
20
|
class="uikit-select__input-container"
|
|
21
21
|
:tabindex="disabled || typeahead ? -1 : 0"
|
|
22
22
|
role="combobox"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
>
|
|
42
42
|
<slot name="selectValue" :item="modelValue">
|
|
43
43
|
{{ modelValue ? modelValue.label : placeholder }}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
<div
|
|
46
46
|
v-if="subtext"
|
|
47
47
|
v-dark="isDarkMode"
|
|
@@ -148,10 +148,10 @@
|
|
|
148
148
|
</div>
|
|
149
149
|
</template>
|
|
150
150
|
|
|
151
|
-
<script lang="ts">
|
|
152
|
-
import { Component, Vue, Prop, Watch, Emit } from 'vue-facing-decorator'
|
|
151
|
+
<script setup lang="ts">
|
|
153
152
|
import Icon from '../Icons/Icon.vue'
|
|
154
|
-
import { dark } from '../../directives'
|
|
153
|
+
import { dark as vDark } from '../../directives'
|
|
154
|
+
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
|
|
155
155
|
|
|
156
156
|
interface IItem {
|
|
157
157
|
value?: string | null
|
|
@@ -160,304 +160,312 @@ interface IItem {
|
|
|
160
160
|
subtext?: string
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
163
|
+
const {
|
|
164
|
+
label = '',
|
|
165
|
+
modelValue = null,
|
|
166
|
+
subtext = '',
|
|
167
|
+
placeholder = '',
|
|
168
|
+
data,
|
|
169
|
+
disabled = false,
|
|
170
|
+
autoFocus = false,
|
|
171
|
+
showEmptyOption = false,
|
|
172
|
+
emptyOptionLabel = '',
|
|
173
|
+
typeahead = false,
|
|
174
|
+
error = false,
|
|
175
|
+
openMenuAbove = false,
|
|
176
|
+
customDropdownTop = 0,
|
|
177
|
+
isDarkMode = false,
|
|
178
|
+
dropdownOverride = false,
|
|
179
|
+
showTypeaheadClear = false,
|
|
180
|
+
} = defineProps<{
|
|
181
|
+
label?: string
|
|
182
|
+
modelValue?: IItem | null
|
|
183
|
+
subtext?: string
|
|
184
|
+
placeholder?: string
|
|
185
|
+
data: IItem[]
|
|
186
|
+
disabled?: boolean
|
|
187
|
+
autoFocus?: boolean
|
|
188
|
+
showEmptyOption?: boolean
|
|
189
|
+
emptyOptionLabel?: string
|
|
190
|
+
typeahead?: boolean
|
|
191
|
+
error?: boolean
|
|
192
|
+
openMenuAbove?: boolean
|
|
193
|
+
customDropdownTop?: number
|
|
194
|
+
isDarkMode?: boolean
|
|
195
|
+
dropdownOverride?: boolean // Can override the icon to be an X instead
|
|
196
|
+
showTypeaheadClear?: boolean
|
|
197
|
+
}>()
|
|
198
|
+
|
|
199
|
+
const emit = defineEmits<{
|
|
200
|
+
'update:modelValue': [modelVal: IItem | null]
|
|
201
|
+
'linkClick': [item: IItem | null]
|
|
202
|
+
'openDropdown': [open: boolean]
|
|
203
|
+
'close': []
|
|
204
|
+
}>()
|
|
205
|
+
|
|
206
|
+
const hover = ref(false)
|
|
207
|
+
const focus = ref(false)
|
|
208
|
+
const showDropdown = ref(false)
|
|
209
|
+
const searchText = ref('')
|
|
210
|
+
const menuHeight = ref<number | null>(null)
|
|
211
|
+
|
|
212
|
+
const inputRef = useTemplateRef<HTMLElement>('uikit-select__input')
|
|
213
|
+
const inputContainerRef = useTemplateRef<HTMLElement>('uikit-select__input-container')
|
|
214
|
+
const menuRef = useTemplateRef<HTMLElement>('menu')
|
|
215
|
+
const itemRefs = useTemplateRef<HTMLElement[]>('uikit-select__items')
|
|
216
|
+
|
|
217
|
+
const filteredData = computed(() => {
|
|
218
|
+
if (typeahead && searchText.value) {
|
|
219
|
+
return data.filter(item => item.label.toLowerCase().includes(searchText.value.toLowerCase()))
|
|
220
|
+
} else {
|
|
221
|
+
if (showEmptyOption && modelValue) {
|
|
222
|
+
return [{ value: null, label: emptyOptionLabel || '' }, ...data ]
|
|
223
|
+
}
|
|
224
|
+
return data
|
|
208
225
|
}
|
|
226
|
+
})
|
|
209
227
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (this.menuHeight) return `-${this.menuHeight}`
|
|
228
|
+
const menuPositionTop = computed(() => {
|
|
229
|
+
if (customDropdownTop) return customDropdownTop
|
|
214
230
|
|
|
215
|
-
|
|
231
|
+
if (menuHeight.value) return `-${menuHeight.value}`
|
|
216
232
|
|
|
217
|
-
|
|
218
|
-
}
|
|
233
|
+
if (subtext) return 60
|
|
219
234
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (this.typeahead) {
|
|
223
|
-
(this.$refs['uikit-select__input'] as HTMLElement).focus()
|
|
224
|
-
} else {
|
|
225
|
-
(this.$refs['uikit-select__input-container'] as HTMLElement).focus()
|
|
226
|
-
}
|
|
227
|
-
}
|
|
235
|
+
return 36
|
|
236
|
+
})
|
|
228
237
|
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
onMounted(() => {
|
|
239
|
+
if (autoFocus) {
|
|
240
|
+
if (typeahead) {
|
|
241
|
+
inputRef.value?.focus()
|
|
242
|
+
} else {
|
|
243
|
+
inputContainerRef.value?.focus()
|
|
231
244
|
}
|
|
232
|
-
|
|
233
|
-
this.updateMenuHeight()
|
|
234
245
|
}
|
|
235
246
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
e.stopPropagation()
|
|
239
|
-
}
|
|
247
|
+
if (typeahead && modelValue?.label) {
|
|
248
|
+
searchText.value = modelValue.label
|
|
240
249
|
}
|
|
241
250
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return
|
|
245
|
-
}
|
|
251
|
+
updateMenuHeight()
|
|
252
|
+
})
|
|
246
253
|
|
|
247
|
-
|
|
248
|
-
|
|
254
|
+
const keydownListener = (e: Event | KeyboardEvent) => {
|
|
255
|
+
if (focus.value && 'key' in e && e.key.match(/^[A-Za-z0-9\s\-_@]$/)) {
|
|
256
|
+
e.stopPropagation()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
249
259
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
this.emitUpdateModelValue(null)
|
|
254
|
-
this.searchText = ''
|
|
255
|
-
} else if (this.typeahead && this.searchText && this.filteredData.length === 1 && this.filteredData[0]) {
|
|
256
|
-
this.emitUpdateModelValue(this.filteredData[0])
|
|
257
|
-
}
|
|
260
|
+
const blurMenu = (e?: FocusEvent) => {
|
|
261
|
+
if ((e?.relatedTarget as Element)?.tagName === 'LI') {
|
|
262
|
+
return
|
|
258
263
|
}
|
|
259
264
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.menuHeight = menu.getBoundingClientRect().height
|
|
271
|
-
}
|
|
272
|
-
if (!originalShowDropdownValue) {
|
|
273
|
-
this.showDropdown = false
|
|
274
|
-
}
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
this.menuHeight = null
|
|
265
|
+
focus.value = false
|
|
266
|
+
showDropdown.value = false
|
|
267
|
+
|
|
268
|
+
if (typeahead && !searchText.value) {
|
|
269
|
+
emitUpdateModelValue(null)
|
|
270
|
+
} else if (typeahead && searchText.value && filteredData.value.length < 1 && !dropdownOverride) {
|
|
271
|
+
emitUpdateModelValue(null)
|
|
272
|
+
searchText.value = ''
|
|
273
|
+
} else if (typeahead && searchText.value && filteredData.value.length === 1 && filteredData.value[0]) {
|
|
274
|
+
emitUpdateModelValue(filteredData.value[0])
|
|
279
275
|
}
|
|
276
|
+
}
|
|
280
277
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const item = this.filteredData.find(i => itemValue ? String(i.value) === itemValue : i.value === null);
|
|
287
|
-
(this.$refs['uikit-select__input-container'] as HTMLElement).focus()
|
|
288
|
-
item && this.selectItem(item)
|
|
289
|
-
}
|
|
290
|
-
// close menu on tab
|
|
291
|
-
if (e.key === 'Tab' && !e.shiftKey) {
|
|
292
|
-
this.blurMenu()
|
|
293
|
-
}
|
|
294
|
-
// navigate items with up key
|
|
295
|
-
if (e.key === 'ArrowUp') {
|
|
296
|
-
e.preventDefault()
|
|
297
|
-
const itemValue = (e.target as HTMLElement).getAttribute('data-value')
|
|
298
|
-
const itemIndex = this.filteredData.findIndex(
|
|
299
|
-
i => itemValue ? String(i.value) === itemValue : i.value === null
|
|
300
|
-
)
|
|
301
|
-
const prevIndex = itemIndex < 1 ? 0 : itemIndex - 1
|
|
302
|
-
const prevValue = this.filteredData[prevIndex]?.value
|
|
303
|
-
const items = this.$refs['uikit-select__items'] as HTMLElement[]
|
|
304
|
-
const prevItem = items.find(
|
|
305
|
-
item => prevValue
|
|
306
|
-
? String(prevValue) === item.getAttribute('data-value')
|
|
307
|
-
: item.getAttribute('data-value') === null
|
|
308
|
-
)
|
|
309
|
-
if (prevItem) {
|
|
310
|
-
prevItem.focus()
|
|
311
|
-
this.showDropdown = true
|
|
312
|
-
this.focus = true
|
|
313
|
-
}
|
|
278
|
+
const updateMenuHeight = () => {
|
|
279
|
+
if (openMenuAbove) {
|
|
280
|
+
const originalShowDropdownValue = showDropdown.value
|
|
281
|
+
if (!showDropdown.value) {
|
|
282
|
+
showDropdown.value = true
|
|
314
283
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const data = this.filteredData
|
|
319
|
-
const itemValue = (e.target as HTMLElement).getAttribute('data-value')
|
|
320
|
-
const itemIndex = data.findIndex(i => itemValue ? String(i.value) === itemValue : i.value === null)
|
|
321
|
-
const nextIndex = itemIndex >= data.length - 1 ? data.length - 1 : itemIndex + 1
|
|
322
|
-
const nextValue = this.filteredData[nextIndex]?.value
|
|
323
|
-
const items = this.$refs['uikit-select__items'] as HTMLElement[]
|
|
324
|
-
const nextItem = items.find(
|
|
325
|
-
item => nextValue
|
|
326
|
-
? String(nextValue) === item.getAttribute('data-value')
|
|
327
|
-
: item.getAttribute('data-value') === null
|
|
328
|
-
)
|
|
329
|
-
if (nextItem) {
|
|
330
|
-
nextItem.focus()
|
|
331
|
-
this.showDropdown = true
|
|
332
|
-
this.focus = true
|
|
284
|
+
nextTick(() => {
|
|
285
|
+
if (menuRef.value) {
|
|
286
|
+
menuHeight.value = menuRef.value.getBoundingClientRect().height || null
|
|
333
287
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
keyPressedContainer (e: KeyboardEvent) {
|
|
338
|
-
if (!(e.target as HTMLElement).className.includes('uikit-select__input-container')
|
|
339
|
-
&& !(e.target as HTMLElement).className.includes('uikit-select__input')
|
|
340
|
-
) {
|
|
341
|
-
return
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// open showDropdown on down arrow and select first item
|
|
345
|
-
if (e.key === 'ArrowDown'
|
|
346
|
-
|| (e.key === 'Tab' && !e.shiftKey && this.typeahead && this.searchText && this.showDropdown)) {
|
|
347
|
-
e.preventDefault()
|
|
348
|
-
const firstValue = this.filteredData[0]?.value
|
|
349
|
-
const items = this.$refs['uikit-select__items'] as HTMLElement[]
|
|
350
|
-
const firstItem = items.find(
|
|
351
|
-
item => firstValue
|
|
352
|
-
? String(firstValue) === item.getAttribute('data-value')
|
|
353
|
-
: item.getAttribute('data-value') === null
|
|
354
|
-
)
|
|
355
|
-
if (firstItem) {
|
|
356
|
-
firstItem.focus()
|
|
357
|
-
this.showDropdown = true
|
|
358
|
-
this.focus = true
|
|
288
|
+
if (!originalShowDropdownValue) {
|
|
289
|
+
showDropdown.value = false
|
|
359
290
|
}
|
|
360
|
-
}
|
|
291
|
+
})
|
|
292
|
+
}
|
|
361
293
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
e.preventDefault()
|
|
365
|
-
this.showDropdown = !this.showDropdown
|
|
366
|
-
}
|
|
294
|
+
menuHeight.value = null
|
|
295
|
+
}
|
|
367
296
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
297
|
+
const keyPressedItem = (e: KeyboardEvent) => {
|
|
298
|
+
// select option on enter or space or tab (but not shift tab)
|
|
299
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
300
|
+
e.preventDefault()
|
|
301
|
+
const itemValue = (e.target as HTMLElement).getAttribute('data-value')
|
|
302
|
+
const item = filteredData.value.find(i => itemValue ? String(i.value) === itemValue : i.value === null)
|
|
303
|
+
inputContainerRef.value?.focus()
|
|
304
|
+
item && selectItem(item)
|
|
374
305
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
306
|
+
// close menu on tab
|
|
307
|
+
if (e.key === 'Tab' && !e.shiftKey) {
|
|
308
|
+
blurMenu()
|
|
378
309
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
310
|
+
// navigate items with up key
|
|
311
|
+
if (e.key === 'ArrowUp') {
|
|
312
|
+
e.preventDefault()
|
|
313
|
+
const itemValue = (e.target as HTMLElement).getAttribute('data-value')
|
|
314
|
+
const itemIndex = filteredData.value.findIndex(
|
|
315
|
+
i => itemValue ? String(i.value) === itemValue : i.value === null
|
|
316
|
+
)
|
|
317
|
+
const prevIndex = itemIndex < 1 ? 0 : itemIndex - 1
|
|
318
|
+
const prevValue = filteredData.value[prevIndex]?.value
|
|
319
|
+
const prevItem = itemRefs.value?.find(
|
|
320
|
+
item => prevValue
|
|
321
|
+
? prevValue === item.getAttribute('data-value')
|
|
322
|
+
: item.getAttribute('data-value') === null
|
|
323
|
+
)
|
|
324
|
+
if (prevItem) {
|
|
325
|
+
prevItem.focus()
|
|
326
|
+
showDropdown.value = true
|
|
327
|
+
focus.value = true
|
|
384
328
|
}
|
|
385
329
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
330
|
+
// navigate items with down key
|
|
331
|
+
if (e.key === 'ArrowDown') {
|
|
332
|
+
e.preventDefault()
|
|
333
|
+
const itemValue = (e.target as HTMLElement).getAttribute('data-value')
|
|
334
|
+
const itemIndex = filteredData.value.findIndex(i =>
|
|
335
|
+
itemValue
|
|
336
|
+
? String(i.value) === itemValue
|
|
337
|
+
: i.value === null
|
|
338
|
+
)
|
|
339
|
+
const nextIndex = itemIndex >= filteredData.value.length - 1 ? filteredData.value.length - 1 : itemIndex + 1
|
|
340
|
+
const nextValue = filteredData.value[nextIndex]?.value
|
|
341
|
+
const nextItem = itemRefs.value?.find(
|
|
342
|
+
item => nextValue
|
|
343
|
+
? nextValue === item.getAttribute('data-value')
|
|
344
|
+
: item.getAttribute('data-value') === null
|
|
345
|
+
)
|
|
346
|
+
if (nextItem) {
|
|
347
|
+
nextItem.focus()
|
|
348
|
+
showDropdown.value = true
|
|
349
|
+
focus.value = true
|
|
390
350
|
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
391
353
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
354
|
+
const keyPressedContainer = (e: KeyboardEvent) => {
|
|
355
|
+
if (!(e.target as HTMLElement).className.includes('uikit-select__input-container')
|
|
356
|
+
&& !(e.target as HTMLElement).className.includes('uikit-select__input')
|
|
357
|
+
) {
|
|
358
|
+
return
|
|
397
359
|
}
|
|
398
360
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
361
|
+
// open showDropdown on down arrow and select first item
|
|
362
|
+
if (e.key === 'ArrowDown'
|
|
363
|
+
|| (e.key === 'Tab' && !e.shiftKey && typeahead && searchText.value && showDropdown.value)) {
|
|
364
|
+
e.preventDefault()
|
|
365
|
+
const firstValue = filteredData.value[0]?.value
|
|
366
|
+
const firstItem = itemRefs.value?.find(
|
|
367
|
+
item => firstValue
|
|
368
|
+
? firstValue === item.getAttribute('data-value')
|
|
369
|
+
: item.getAttribute('data-value') === null
|
|
370
|
+
)
|
|
371
|
+
if (firstItem) {
|
|
372
|
+
firstItem.focus()
|
|
373
|
+
showDropdown.value = true
|
|
374
|
+
focus.value = true
|
|
410
375
|
}
|
|
411
376
|
}
|
|
412
377
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
378
|
+
// toggle showDropdown on enter or space + not typeahead
|
|
379
|
+
if (e.key === 'Enter' || (e.key === ' ' && !typeahead)) {
|
|
380
|
+
e.preventDefault()
|
|
381
|
+
showDropdown.value = !showDropdown.value
|
|
416
382
|
}
|
|
417
383
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
384
|
+
// escape to close dropdown
|
|
385
|
+
if (e.key === 'Escape') {
|
|
386
|
+
e.preventDefault()
|
|
387
|
+
e.stopPropagation()
|
|
388
|
+
showDropdown.value = false
|
|
423
389
|
}
|
|
390
|
+
}
|
|
424
391
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const matchedItem = this.filteredData.find(item =>
|
|
429
|
-
item.label.toLowerCase() === newVal.toLowerCase()
|
|
430
|
-
)
|
|
431
|
-
if (!matchedItem) {
|
|
432
|
-
this.showDropdown = true
|
|
433
|
-
}
|
|
434
|
-
if (this.filteredData.length === 1 && matchedItem && this.modelValue?.label !== matchedItem.label) {
|
|
435
|
-
this.selectItem(matchedItem)
|
|
436
|
-
}
|
|
392
|
+
const mouseOverSelect = () => {
|
|
393
|
+
hover.value = disabled ? false : true
|
|
394
|
+
}
|
|
437
395
|
|
|
438
|
-
|
|
439
|
-
|
|
396
|
+
const focusSelect = () => {
|
|
397
|
+
focus.value = disabled ? false : true
|
|
398
|
+
if (typeahead && !searchText.value) {
|
|
399
|
+
showDropdown.value = true
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const selectItem = (item: IItem) => {
|
|
404
|
+
if (item) {
|
|
405
|
+
showDropdown.value = false
|
|
440
406
|
}
|
|
441
407
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
408
|
+
if (item.type === 'link') {
|
|
409
|
+
emitLinkClick(item.value ? item : null)
|
|
410
|
+
} else {
|
|
411
|
+
emitUpdateModelValue(item.value ? item : null)
|
|
445
412
|
}
|
|
413
|
+
}
|
|
446
414
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
415
|
+
const performCloseActions = () => {
|
|
416
|
+
/**
|
|
417
|
+
* If the dropdown is showing an X, there is some logic we need...
|
|
418
|
+
* The first time, it should clear out search text -- the second time it should collapse dropdown
|
|
419
|
+
* */
|
|
420
|
+
if (searchText.value) {
|
|
421
|
+
emitUpdateModelValue(null)
|
|
422
|
+
searchText.value = ''
|
|
423
|
+
} else {
|
|
424
|
+
showDropdown.value = false
|
|
425
|
+
emitClose()
|
|
450
426
|
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
watch(showDropdown, (newVal: boolean) => {
|
|
430
|
+
emitOpenDropdown(newVal)
|
|
431
|
+
})
|
|
451
432
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
433
|
+
watch(() => modelValue, (newVal) => {
|
|
434
|
+
if (typeahead) {
|
|
435
|
+
searchText.value = newVal?.label || ''
|
|
455
436
|
}
|
|
437
|
+
})
|
|
456
438
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
439
|
+
watch(searchText, (newVal: string) => {
|
|
440
|
+
if (typeahead) {
|
|
441
|
+
const matchedItem = filteredData.value.find(item =>
|
|
442
|
+
item.label.toLowerCase() === newVal.toLowerCase()
|
|
443
|
+
)
|
|
444
|
+
if (!matchedItem) {
|
|
445
|
+
showDropdown.value = true
|
|
446
|
+
}
|
|
447
|
+
if (filteredData.value.length === 1 && matchedItem && modelValue?.label !== matchedItem.label) {
|
|
448
|
+
selectItem(matchedItem)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
updateMenuHeight()
|
|
460
452
|
}
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
const emitUpdateModelValue = (item: IItem | null) => {
|
|
456
|
+
emit('update:modelValue', item)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const emitLinkClick = (item: IItem | null) => {
|
|
460
|
+
emit('linkClick', item)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const emitOpenDropdown = (open: boolean) => {
|
|
464
|
+
emit('openDropdown', open)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const emitClose = () => {
|
|
468
|
+
emit('close')
|
|
461
469
|
}
|
|
462
470
|
</script>
|
|
463
471
|
|
|
@@ -613,7 +621,7 @@ export default class Select extends Vue {
|
|
|
613
621
|
border-color: $pewter;
|
|
614
622
|
color: $fog;
|
|
615
623
|
caret-color: $banana-bread;
|
|
616
|
-
|
|
624
|
+
|
|
617
625
|
&--hover {
|
|
618
626
|
background-color: $charcoal;
|
|
619
627
|
}
|