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:
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user