2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-22 10:30:34 +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> <template>
<div id="app"> <div class="flex items-center justify-center pt-40">
<ListBox <ListBox
v-model="selected" v-model="selected"
:open="open" v-model:open="open"
class="px-2 text-left border border-gray-500 rounded w-64 relative bg-blue-100 h-12" class="px-2 text-left border border-gray-500 rounded w-64 relative h-12"
> >
{{ selected?.label }} {{ selected?.label }}
<ListBoxMenu <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 <ListBoxOption
@click="selected = country" @click="selected = country"
v-for="country in config.options" v-for="country in config.options"
:key="country.id" :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> </ListBoxOption>
</ListBoxMenu> </ListBoxMenu>
</ListBox> </ListBox>
@@ -33,6 +37,7 @@ export default {
components: { ListBoxOption, ListBoxMenu, ListBox }, components: { ListBoxOption, ListBoxMenu, ListBox },
data: () => ({ data: () => ({
selected: null, selected: null,
open: false,
config: { config: {
options: countries, options: countries,
@@ -40,26 +45,3 @@ export default {
}), }),
} }
</script> </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 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. 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 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.
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.
#### Example: Open the dropdown when search text is present #### Example: Open the dropdown when search text is present
--- ---
+30 -10
View File
@@ -1,25 +1,45 @@
<script setup lang="ts"> <script setup lang="ts">
import { ListBoxInjectionKey } from '@/keys.js' import { ListBoxInjectionKey } from '@/keys'
import type { ComputedRef, PropType } from 'vue' import type { ComputedRef, PropType } from 'vue'
import { provide, defineEmits, computed, ref } from 'vue' import { provide, defineEmits, computed, reactive, watch } from 'vue'
export interface ListBoxProps { export interface ListBoxProps {
modelValue: PropType<unknown> 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>(), { 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, ListBoxInjectionKey,
computed<ListBoxProps>(() => ({ computed<ResolvedListBoxProps>(() => ({
open: props.open, open: isOpen.value,
modelValue: props.modelValue, modelValue: props.modelValue,
})) }))
) )
@@ -31,7 +51,7 @@ provide<ComputedListBoxInjectionProps>(
type="button" type="button"
aria-haspopup="true" aria-haspopup="true"
:aria-expanded="open" :aria-expanded="open"
@click.exact="open = !open" @click.exact="mutableState.open = !mutableState.open"
> >
<slot></slot> <slot></slot>
</button> </button>
+3 -3
View File
@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, inject } from 'vue' import { defineProps, inject } from 'vue'
import { ListBoxInjectionKey } from '@/keys' import { ListBoxInjectionKey } from '@/keys'
import type { ComputedListBoxInjectionProps } from '@/components/ListBox/ListBox.vue' import type { InjectedListBoxProps } from '@/components/ListBox/ListBox.vue'
withDefaults( withDefaults(
defineProps<{ defineProps<{
@@ -12,11 +12,11 @@ withDefaults(
} }
) )
const listBoxProps = inject<ComputedListBoxInjectionProps>(ListBoxInjectionKey) const listBoxProps = inject<InjectedListBoxProps>(ListBoxInjectionKey)
</script> </script>
<template> <template>
<Component :is="as" v-show="listBoxProps?.open"> <Component :is="as" v-show="listBoxProps.open">
<slot></slot> <slot></slot>
</Component> </Component>
</template> </template>
+12 -3
View File
@@ -1,18 +1,27 @@
<script setup lang="ts"> <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<{ defineProps<{
as?: string as?: string
value: unknown
}>(), }>(),
{ {
as: 'div', as: 'div',
} }
) )
const listBoxProps = inject<InjectedListBoxProps>(ListBoxInjectionKey)
const isSelected = computed(() => {
return listBoxProps?.value.modelValue === props.value
})
</script> </script>
<template> <template>
<Component :is="as"> <Component :is="as">
<slot></slot> <slot v-bind="{ isSelected }"></slot>
</Component> </Component>
</template> </template>
View File