2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-07 07:12:23 +03:00

allow open prop to control menu state

This commit is contained in:
Jeff Sagal
2022-11-16 20:55:04 -08:00
parent 6c9a480a04
commit 5a5e9378d9
6 changed files with 58 additions and 50 deletions
+11 -29
View File
@@ -1,22 +1,26 @@
<template>
<div id="app">
<div class="flex items-center justify-center pt-40">
<ListBox
v-model="selected"
:open="open"
class="px-2 text-left border border-gray-500 rounded w-64 relative bg-blue-100 h-12"
v-model:open="open"
class="px-2 text-left border border-gray-500 rounded w-64 relative h-12"
>
{{ selected?.label }}
<ListBoxMenu
class="absolute inset-0 top-12 w-full h-64 overflow-y-scroll bg-red-100 space-y-1"
class="absolute inset-0 top-12 w-full h-64 overflow-y-scroll space-y-1 border rounded"
>
<ListBoxOption
@click="selected = country"
v-for="country in config.options"
:key="country.id"
class="px-2 py-1"
:class="['px-2 py-1']"
:value="country"
#default="{ isSelected }"
>
{{ country.label }}
<span :class="{ 'text-indigo-600': isSelected }">
{{ country.label }}
</span>
</ListBoxOption>
</ListBoxMenu>
</ListBox>
@@ -33,6 +37,7 @@ export default {
components: { ListBoxOption, ListBoxMenu, ListBox },
data: () => ({
selected: null,
open: false,
config: {
options: countries,
@@ -40,26 +45,3 @@ export default {
}),
}
</script>
<style>
html,
body {
margin: 0;
height: 100%;
font-family: -apple-system, sans-serif;
}
#app {
height: 100%;
max-width: 20rem;
margin: 10rem auto 0;
}
hr {
border: none;
border-bottom: 1px solid #cacaca;
margin-bottom: 1em;
padding-top: 1em;
width: 90%;
}
</style>
+2 -5
View File
@@ -3,12 +3,9 @@
By default, the dropdown will open anytime the underlying search input has focus. The dropdown will
open when clicked, or when it has received focus when tabbing through inputs.
## Customizing Dropdown Behaviour <Badge text="v3.12.0+" />
## Customizing Dropdown Behaviour <Badge text="v4+" />
The `dropdownShouldOpen` prop allows for full customization of the open/close behaviour. The prop
accepts a `function` that should return a `boolean` value. The returned boolean value will be used
to determine if the dropdown should be `open`/`true` or `false`/`closed`. The function receives the
instance of the component as the only argument.
The `open` prop can control showing and hiding the dropdown menu from a parent component. If this prop is set, the component will always use the value of `props.open` to handle showing and hiding the dropdown. Vue Select will emit the `update:open` when the prop value should change, so you can use `<VueSelect v-model:open="open" />` to have control over the `open` state while preserving default behaviour.
#### Example: Open the dropdown when search text is present
---
+30 -10
View File
@@ -1,25 +1,45 @@
<script setup lang="ts">
import { ListBoxInjectionKey } from '@/keys.js'
import { ListBoxInjectionKey } from '@/keys'
import type { ComputedRef, PropType } from 'vue'
import { provide, defineEmits, computed, ref } from 'vue'
import { provide, defineEmits, computed, reactive, watch } from 'vue'
export interface ListBoxProps {
modelValue: PropType<unknown>
open?: boolean
open?: boolean | undefined
}
export interface ResolvedListBoxProps extends Omit<ListBoxProps, 'open'> {
open: boolean
}
export type InjectedListBoxProps = ComputedRef<ResolvedListBoxProps>
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'update:open', 'open', 'close'])
const props = withDefaults(defineProps<ListBoxProps>(), {
open: false,
open: undefined,
})
export type ComputedListBoxInjectionProps = ComputedRef<ListBoxProps>
const mutableState = reactive<{
open: boolean
}>({
open: props.open === undefined ? false : props.open,
})
provide<ComputedListBoxInjectionProps>(
watch(
() => mutableState.open,
(open) => emit('update:open', open)
)
const isOpen = computed<boolean>(() => {
if (props.open !== undefined) {
return props.open
}
return mutableState.open
})
provide<InjectedListBoxProps>(
ListBoxInjectionKey,
computed<ListBoxProps>(() => ({
open: props.open,
computed<ResolvedListBoxProps>(() => ({
open: isOpen.value,
modelValue: props.modelValue,
}))
)
@@ -31,7 +51,7 @@ provide<ComputedListBoxInjectionProps>(
type="button"
aria-haspopup="true"
:aria-expanded="open"
@click.exact="open = !open"
@click.exact="mutableState.open = !mutableState.open"
>
<slot></slot>
</button>
+3 -3
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { defineProps, inject } from 'vue'
import { ListBoxInjectionKey } from '@/keys'
import type { ComputedListBoxInjectionProps } from '@/components/ListBox/ListBox.vue'
import type { InjectedListBoxProps } from '@/components/ListBox/ListBox.vue'
withDefaults(
defineProps<{
@@ -12,11 +12,11 @@ withDefaults(
}
)
const listBoxProps = inject<ComputedListBoxInjectionProps>(ListBoxInjectionKey)
const listBoxProps = inject<InjectedListBoxProps>(ListBoxInjectionKey)
</script>
<template>
<Component :is="as" v-show="listBoxProps?.open">
<Component :is="as" v-show="listBoxProps.open">
<slot></slot>
</Component>
</template>
+12 -3
View File
@@ -1,18 +1,27 @@
<script setup lang="ts">
import { defineProps } from 'vue'
import { computed, defineProps, inject } from 'vue'
import type { InjectedListBoxProps } from '@/components/ListBox/ListBox.vue'
import { ListBoxInjectionKey } from '@/keys'
withDefaults(
const props = withDefaults(
defineProps<{
as?: string
value: unknown
}>(),
{
as: 'div',
}
)
const listBoxProps = inject<InjectedListBoxProps>(ListBoxInjectionKey)
const isSelected = computed(() => {
return listBoxProps?.value.modelValue === props.value
})
</script>
<template>
<Component :is="as">
<slot></slot>
<slot v-bind="{ isSelected }"></slot>
</Component>
</template>
View File