2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-22 10:30:34 +03:00

split up provided contexts

This commit is contained in:
Jeff Sagal
2022-12-01 12:20:11 -08:00
parent eaced8caf1
commit f32ef69e3d
5 changed files with 78 additions and 84 deletions
+11 -27
View File
@@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject, onMounted, ref, watch } from 'vue' import { inject, onMounted, ref, watch } from 'vue'
import vAppendToBody from '@/directives/appendToBody.js' import vAppendToBody from '@/directives/appendToBody.js'
import { VueSelectInjectionKey } from '@/symbols' import { DropdownMenuKey } from '@/symbols'
import type { InjectedVueSelectContext } from '@/types' import type { InjectedDropdownMenuContext } from '@/types'
const context = inject<InjectedVueSelectContext>(VueSelectInjectionKey) const context = inject<InjectedDropdownMenuContext>(DropdownMenuKey)
const dropdownMenu = ref<HTMLElement | null>(null) const dropdownMenu = ref<HTMLElement | null>(null)
onMounted(() => { onMounted(() => {
@@ -28,7 +28,7 @@ withDefaults(defineProps<{ as?: string }>(), { as: 'ul' })
<template> <template>
<Component <Component
:is="as" :is="as"
v-show="context.dropdownOpen" v-if="context.dropdownOpen"
:id="`vs${context.uid}__listbox`" :id="`vs${context.uid}__listbox`"
ref="dropdownMenu" ref="dropdownMenu"
:key="`vs${context.uid}__listbox`" :key="`vs${context.uid}__listbox`"
@@ -40,28 +40,12 @@ withDefaults(defineProps<{ as?: string }>(), { as: 'ul' })
@mouseup="context.onMouseUp" @mouseup="context.onMouseUp"
> >
<slot></slot> <slot></slot>
<!-- TODO: not sure why, but using the previous system of swapping the dropdown el at open causes performance to drop drastically with Vue dev tools open -->
<!-- <Component-->
<!-- :is="as"-->
<!-- v-if="context.dropdownOpen"-->
<!-- :id="`vs${context.uid}__listbox`"-->
<!-- ref="dropdownMenu"-->
<!-- :key="`vs${context.uid}__listbox`"-->
<!-- v-append-to-body-->
<!-- class="vs__dropdown-menu"-->
<!-- role="listbox"-->
<!-- tabindex="-1"-->
<!-- @mousedown.prevent="context.onMousedown"-->
<!-- @mouseup="context.onMouseUp"-->
<!-- >-->
<!-- <slot></slot>-->
<!-- </Component>-->
<!-- <Component-->
<!-- :is="as"-->
<!-- v-else-->
<!-- :id="`vs${context.uid}__listbox`"-->
<!-- role="listbox"-->
<!-- style="display: none; visibility: hidden"-->
<!-- ></Component>-->
</Component> </Component>
<Component
:is="as"
v-else
:id="`vs${context.uid}__listbox`"
role="listbox"
style="display: none; visibility: hidden"
></Component>
</template> </template>
+14 -17
View File
@@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject, onBeforeUnmount, onMounted, ref } from 'vue' import { computed, inject, ref } from 'vue'
import { VueSelectInjectionKey } from '@/symbols' import { DropdownMenuItemKey } from '@/symbols'
import type { InjectedVueSelectContext, VueSelectOption } from '@/types' import type { InjectedDropdownMenuItemContext, VueSelectOption } from '@/types'
const context = inject<InjectedVueSelectContext>(VueSelectInjectionKey) const context = inject<InjectedDropdownMenuItemContext>(DropdownMenuItemKey)
interface Props { interface Props {
as?: string as?: string
@@ -12,26 +12,23 @@ interface Props {
opinionated?: boolean opinionated?: boolean
} }
withDefaults(defineProps<Props>(), { as: 'li', opinionated: true }) const props = withDefaults(defineProps<Props>(), {
as: 'li',
const selectableOption = ref<HTMLElement | null>(null) opinionated: true,
onMounted(() => {
if (selectableOption.value) {
context?.value.registerSelectableEl(selectableOption.value)
}
}) })
onBeforeUnmount(() => { const optionKey = computed(() => {
if (selectableOption.value) { return context?.value.getOptionKey(props.option)
context?.value.unRegisterSelectableEl(selectableOption.value) })
}
const shouldDisplay = computed(() => {
return context?.value.filteredOptionKeys.includes(optionKey.value)
}) })
</script> </script>
<template> <template>
<Component <Component
ref="selectableOption" v-if="shouldDisplay"
:is="as" :is="as"
:id="`vs${context.uid}__option-${index}`" :id="`vs${context.uid}__option-${index}`"
role="option" role="option"
+35 -29
View File
@@ -117,10 +117,17 @@ import ajax from '@/mixins/ajax.js'
import childComponents from '@/components/childComponents.js' import childComponents from '@/components/childComponents.js'
import sortAndStringify from '@/utility/sortAndStringify.js' import sortAndStringify from '@/utility/sortAndStringify.js'
import uniqueId from '@/utility/uniqueId.js' import uniqueId from '@/utility/uniqueId.js'
import { computed, defineComponent } from 'vue' import { computed, defineComponent, PropType } from 'vue'
import { VueSelectInjectionKey } from '@/symbols.js' import { DropdownMenuItemKey, DropdownMenuKey } from '@/symbols.js'
import DropdownMenu from '@/components/DropdownMenu.vue' import DropdownMenu from '@/components/DropdownMenu.vue'
import type { VueSelectContext, VueSelectOption } from '@/types' import type {
DropdownMenuContext,
DropdownMenuItemContext,
RegisteredOption,
SelectableElement,
VueSelectOption,
VueSelectOptionKey,
} from '@/types'
import DropdownMenuItem from '@/components/DropdownMenuItem.vue' import DropdownMenuItem from '@/components/DropdownMenuItem.vue'
export default defineComponent({ export default defineComponent({
@@ -309,7 +316,7 @@ export default defineComponent({
* @since 3.3.0 * @since 3.3.0
*/ */
selectable: { selectable: {
type: Function, type: Function as PropType<(option: VueSelectOption) => boolean>,
default: (option: VueSelectOption): boolean => true, default: (option: VueSelectOption): boolean => true,
}, },
@@ -327,7 +334,7 @@ export default defineComponent({
* @return {String} * @return {String}
*/ */
getOptionLabel: { getOptionLabel: {
type: Function, type: Function as PropType<(option: VueSelectOption) => string>,
default(option: VueSelectOption) { default(option: VueSelectOption) {
if (typeof option === 'object') { if (typeof option === 'object') {
if (!option.hasOwnProperty(this.label)) { if (!option.hasOwnProperty(this.label)) {
@@ -354,13 +361,9 @@ export default defineComponent({
* slow with lots of objects. * slow with lots of objects.
* *
* The result of this function *must* be unique. * The result of this function *must* be unique.
*
* @type {Function}
* @param {Object || String} option
* @return {String}
*/ */
getOptionKey: { getOptionKey: {
type: Function, type: Function as PropType<(option: VueSelectOption) => unknown>,
default(option: VueSelectOption): unknown { default(option: VueSelectOption): unknown {
if (typeof option !== 'object') { if (typeof option !== 'object') {
return option return option
@@ -587,7 +590,6 @@ export default defineComponent({
* for the search input. Can be used to implement * for the search input. Can be used to implement
* custom behaviour for key presses. * custom behaviour for key presses.
*/ */
mapKeydown: { mapKeydown: {
type: Function, type: Function,
/** /**
@@ -663,29 +665,31 @@ export default defineComponent({
provide() { provide() {
return { return {
[VueSelectInjectionKey]: computed<VueSelectContext>(() => { [DropdownMenuKey]: computed<DropdownMenuContext>(() => {
return { const context: DropdownMenuContext = {
uid: this.uid,
dropdownOpen: this.dropdownOpen,
onMousedown: this.onMousedown,
onMouseup: this.onMouseUp,
setDropdownMenuEl: (el: HTMLElement | null) => {
this.dropdownMenuEl = el
},
}
return context
}),
[DropdownMenuItemKey]: computed<DropdownMenuItemContext>(() => {
const context: DropdownMenuItemContext = {
uid: this.uid, uid: this.uid,
getOptionKey: this.getOptionKey, getOptionKey: this.getOptionKey,
isOptionDeselectable: this.isOptionDeselectable, isOptionDeselectable: this.isOptionDeselectable,
isOptionSelected: this.isOptionSelected, isOptionSelected: this.isOptionSelected,
typeAheadPointer: this.typeAheadPointer, typeAheadPointer: this.typeAheadPointer,
setTypeAheadPointer: this.setTypeAheadPointer, setTypeAheadPointer: this.setTypeAheadPointer,
dropdownOpen: this.dropdownOpen, filteredOptionKeys: this.filteredOptionKeys,
onMousedown: this.onMousedown,
onMouseup: this.onMouseUp,
selectable: this.selectable, selectable: this.selectable,
select: this.select, 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)
},
} }
return context
}), }),
} }
}, },
@@ -700,7 +704,6 @@ export default defineComponent({
_value: [], // Internal value managed by Vue Select if no `value` prop is passed _value: [], // Internal value managed by Vue Select if no `value` prop is passed
deselectButtons: [], deselectButtons: [],
dropdownMenuEl: null, dropdownMenuEl: null,
selectableEls: [],
} as { } as {
search: string search: string
open: boolean open: boolean
@@ -709,7 +712,6 @@ export default defineComponent({
_value: any[] _value: any[]
deselectButtons: any[] deselectButtons: any[]
dropdownMenuEl: HTMLElement | null dropdownMenuEl: HTMLElement | null
selectableEls: HTMLElement[]
} }
}, },
@@ -752,7 +754,7 @@ export default defineComponent({
* *
* @return {Array} * @return {Array}
*/ */
optionList() { optionList(): VueSelectOption[] {
return this.options.concat(this.pushTags ? this.pushedTags : []) return this.options.concat(this.pushTags ? this.pushedTags : [])
}, },
@@ -899,7 +901,7 @@ export default defineComponent({
* *
* @return {array} * @return {array}
*/ */
filteredOptions() { filteredOptions(): VueSelectOption[] {
const optionList = [].concat(this.optionList) const optionList = [].concat(this.optionList)
if (!this.filterable && !this.taggable) { if (!this.filterable && !this.taggable) {
@@ -918,6 +920,10 @@ export default defineComponent({
return options return options
}, },
filteredOptionKeys(): VueSelectOptionKey[] {
return this.filteredOptions.map((option) => this.getOptionKey(option))
},
/** /**
* Check if there aren't any options selected. * Check if there aren't any options selected.
* @return {Boolean} * @return {Boolean}
+3 -3
View File
@@ -1,5 +1,5 @@
import type { InjectionKey } from 'vue' import type { InjectionKey } from 'vue'
export const VueSelectInjectionKey = Symbol( export const DropdownMenuKey = Symbol('DropdownMenuKey')
'VueSelectInjectionKey'
) as InjectionKey<string> export const DropdownMenuItemKey = Symbol('DropdownMenuItemKey')
+15 -8
View File
@@ -1,20 +1,27 @@
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
export interface VueSelectContext { export interface DropdownMenuContext {
uid: string | number | undefined uid: string | number | undefined
dropdownOpen: boolean dropdownOpen: boolean
onMousedown: (e: MouseEvent) => void
onMouseup: (e: MouseEvent) => void
setDropdownMenuEl: (el: HTMLElement | null) => void
}
export type InjectedDropdownMenuContext = ComputedRef<DropdownMenuContext>
export interface DropdownMenuItemContext {
uid: DropdownMenuContext['uid']
typeAheadPointer: number typeAheadPointer: number
setTypeAheadPointer: (index: number) => void setTypeAheadPointer: (index: number) => void
isOptionDeselectable: (option: VueSelectOption) => boolean isOptionDeselectable: (option: VueSelectOption) => boolean
isOptionSelected: (option: VueSelectOption) => boolean isOptionSelected: (option: VueSelectOption) => boolean
onMousedown: (e: MouseEvent) => void getOptionKey: (option: VueSelectOption) => unknown
onMouseup: (e: MouseEvent) => void filteredOptionKeys: VueSelectOptionKey[]
select: (option: VueSelectOption) => void select: (option: VueSelectOption) => void
setDropdownMenuEl: (el: HTMLElement | null) => void selectable: (option: VueSelectOption) => boolean
registerSelectableEl: (el: HTMLElement | null) => void
unRegisterSelectableEl: (el: HTMLElement | null) => void
} }
export type InjectedDropdownMenuItemContext =
export type InjectedVueSelectContext = ComputedRef<VueSelectContext> ComputedRef<DropdownMenuItemContext>
export type VueSelectOption = unknown export type VueSelectOption = unknown
export type VueSelectOptionKey = unknown