From c2d69c3517a18484c6328fa98cc00486bce91984 Mon Sep 17 00:00:00 2001 From: Jeff Sagal Date: Wed, 30 Nov 2022 16:22:54 -0800 Subject: [PATCH] wip - hitting perf issues with devtools open --- dev/Dev.vue | 55 ++++++++++- src/components/DropdownMenu.vue | 60 +++++++++--- src/components/DropdownMenuItem.vue | 64 +++++++++++++ src/components/Select.vue | 112 +++++++++++++---------- src/mixins/pointerScroll.js | 14 ++- src/types.ts | 20 ++++ src/utility/{uniqueId.js => uniqueId.ts} | 2 +- 7 files changed, 258 insertions(+), 69 deletions(-) create mode 100644 src/components/DropdownMenuItem.vue create mode 100644 src/types.ts rename src/utility/{uniqueId.js => uniqueId.ts} (83%) diff --git a/dev/Dev.vue b/dev/Dev.vue index ab8e964..b6f4164 100644 --- a/dev/Dev.vue +++ b/dev/Dev.vue @@ -1,21 +1,72 @@ diff --git a/src/components/DropdownMenu.vue b/src/components/DropdownMenu.vue index c6ec331..c2dd662 100644 --- a/src/components/DropdownMenu.vue +++ b/src/components/DropdownMenu.vue @@ -1,15 +1,34 @@ diff --git a/src/components/DropdownMenuItem.vue b/src/components/DropdownMenuItem.vue new file mode 100644 index 0000000..2d1f7d9 --- /dev/null +++ b/src/components/DropdownMenuItem.vue @@ -0,0 +1,64 @@ + + + diff --git a/src/components/Select.vue b/src/components/Select.vue index ae5267a..3cdf167 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -82,36 +82,30 @@ - - -
  • - - {{ getOptionLabel(option) }} + + + + + + + {{ getOptionLabel(option) }} + + +
  • + + Sorry, no matching options. + +
  • - -
  • - - Sorry, no matching options. - -
  • -
    -
    + + + @@ -123,19 +117,14 @@ import ajax from '@/mixins/ajax.js' import childComponents from '@/components/childComponents.js' import sortAndStringify from '@/utility/sortAndStringify.js' import uniqueId from '@/utility/uniqueId.js' -import { computed, ComputedRef, defineComponent } from 'vue' +import { computed, defineComponent } from 'vue' import { VueSelectInjectionKey } from '@/symbols.js' import DropdownMenu from '@/components/DropdownMenu.vue' - -export interface VueSelectContext { - uid: ComputedRef - dropdownOpen: ComputedRef - onMousedown: (e: MouseEvent) => void - onMouseup: (e: MouseEvent) => void -} +import type { VueSelectContext, VueSelectOption } from '@/types' +import DropdownMenuItem from '@/components/DropdownMenuItem.vue' export default defineComponent({ - components: { DropdownMenu, ...childComponents }, + components: { DropdownMenuItem, DropdownMenu, ...childComponents }, mixins: [pointerScroll, typeAheadPointer, ajax], @@ -309,7 +298,7 @@ export default defineComponent({ */ reduce: { type: Function, - default: (option) => option, + default: (option: VueSelectOption) => option, }, /** @@ -318,12 +307,10 @@ export default defineComponent({ * * @type {Function} * @since 3.3.0 - * @param {Object|String} option - * @return {Boolean} */ selectable: { type: Function, - default: (option) => true, + default: (option: VueSelectOption): boolean => true, }, /** @@ -341,7 +328,7 @@ export default defineComponent({ */ getOptionLabel: { type: Function, - default(option) { + default(option: VueSelectOption) { if (typeof option === 'object') { if (!option.hasOwnProperty(this.label)) { return console.warn( @@ -374,7 +361,7 @@ export default defineComponent({ */ getOptionKey: { type: Function, - default(option) { + default(option: VueSelectOption): unknown { if (typeof option !== 'object') { return option } @@ -659,7 +646,7 @@ export default defineComponent({ */ dropdownShouldOpen: { type: Function, - default({ noDrop, open, mutableLoading }) { + default({ noDrop, open, mutableLoading }): boolean { return noDrop ? false : open && !mutableLoading }, }, @@ -676,12 +663,28 @@ export default defineComponent({ provide() { return { - [VueSelectInjectionKey]: computed(() => { + [VueSelectInjectionKey]: computed(() => { return { uid: this.uid, + getOptionKey: this.getOptionKey, + isOptionDeselectable: this.isOptionDeselectable, + isOptionSelected: this.isOptionSelected, + typeAheadPointer: this.typeAheadPointer, + setTypeAheadPointer: this.setTypeAheadPointer, dropdownOpen: this.dropdownOpen, onMousedown: this.onMousedown, onMouseup: this.onMouseUp, + selectable: this.selectable, + select: this.select, + setDropdownMenuEl: (ref: HTMLElement) => { + this.dropdownMenuEl = ref + }, + registerSelectableEl: (ref: HTMLElement) => { + this.selectableEls.push(ref) + }, + unRegisterSelectableEl: (ref: HTMLElement) => { + this.selectableEls = this.selectableEls.filter((el) => el !== ref) + }, } }), } @@ -696,6 +699,17 @@ export default defineComponent({ // eslint-disable-next-line vue/no-reserved-keys _value: [], // Internal value managed by Vue Select if no `value` prop is passed deselectButtons: [], + dropdownMenuEl: null, + selectableEls: [], + } as { + search: string + open: boolean + isComposing: boolean + pushedTags: VueSelectOption[] + _value: any[] + deselectButtons: any[] + dropdownMenuEl: HTMLElement | null + selectableEls: HTMLElement[] } }, @@ -862,7 +876,7 @@ export default defineComponent({ * dropdown menu. * @return {Boolean} True if open */ - dropdownOpen() { + dropdownOpen(): boolean { return this.dropdownShouldOpen(this) }, @@ -1131,7 +1145,7 @@ export default defineComponent({ * @param {Object|String} option * @return {Boolean} True when selected | False otherwise */ - isOptionSelected(option) { + isOptionSelected(option: VueSelectOption): boolean { return this.selectedValue.some((value) => this.optionComparator(value, option) ) @@ -1140,7 +1154,7 @@ export default defineComponent({ /** * Can the current option be removed via the dropdown? */ - isOptionDeselectable(option) { + isOptionDeselectable(option: VueSelectOption) { return this.isOptionSelected(option) && this.deselectFromDropdown }, diff --git a/src/mixins/pointerScroll.js b/src/mixins/pointerScroll.js index c60619a..aa9019a 100644 --- a/src/mixins/pointerScroll.js +++ b/src/mixins/pointerScroll.js @@ -20,6 +20,10 @@ export default { }, methods: { + setTypeAheadPointer(index) { + this.typeAheadPointer = index + }, + /** * Adjust the scroll position of the dropdown list * if the current pointer is outside of the @@ -28,16 +32,16 @@ export default { */ maybeAdjustScroll() { const optionEl = - this.$refs.dropdownMenu?.children[this.typeAheadPointer] || false + this.dropdownMenuEl?.children[this.typeAheadPointer] || false if (optionEl) { const bounds = this.getDropdownViewport() const { top, bottom, height } = optionEl.getBoundingClientRect() if (top < bounds.top) { - return (this.$refs.dropdownMenu.scrollTop = optionEl.offsetTop) + return (this.dropdownMenuEl.scrollTop = optionEl.offsetTop) } else if (bottom > bounds.bottom) { - return (this.$refs.dropdownMenu.scrollTop = + return (this.dropdownMenuEl.scrollTop = optionEl.offsetTop - (bounds.height - height)) } } @@ -48,8 +52,8 @@ export default { * @returns {{top: (string|*|number), bottom: *}} */ getDropdownViewport() { - return this.$refs.dropdownMenu - ? this.$refs.dropdownMenu.getBoundingClientRect() + return this.dropdownMenuEl + ? this.dropdownMenuEl.getBoundingClientRect() : { height: 0, top: 0, diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..03bcda5 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,20 @@ +import type { ComputedRef } from 'vue' + +export interface VueSelectContext { + uid: string | number | undefined + dropdownOpen: boolean + typeAheadPointer: number + setTypeAheadPointer: (index: number) => void + isOptionDeselectable: (option: VueSelectOption) => boolean + isOptionSelected: (option: VueSelectOption) => boolean + onMousedown: (e: MouseEvent) => void + onMouseup: (e: MouseEvent) => void + select: (option: VueSelectOption) => void + setDropdownMenuEl: (el: HTMLElement | null) => void + registerSelectableEl: (el: HTMLElement | null) => void + unRegisterSelectableEl: (el: HTMLElement | null) => void +} + +export type InjectedVueSelectContext = ComputedRef + +export type VueSelectOption = unknown diff --git a/src/utility/uniqueId.js b/src/utility/uniqueId.ts similarity index 83% rename from src/utility/uniqueId.js rename to src/utility/uniqueId.ts index 53de592..83f9f45 100644 --- a/src/utility/uniqueId.js +++ b/src/utility/uniqueId.ts @@ -5,7 +5,7 @@ let idCount = 0 * Thanks lodash! * @return {number} */ -function uniqueId() { +function uniqueId(): number { return ++idCount }