Combobox(自動完成)
Combobox 是應用程式中無障礙自動完成和指令面板的基礎,支援健全的鍵盤導覽功能。
首先,透過 npm 安裝 Headless UI。
請注意:此函式庫僅支援 Vue 3。
npm install @headlessui/vue
Combobox 使用 Combobox
、ComboboxInput
、ComboboxButton
、ComboboxOptions
、ComboboxOption
和 ComboboxLabel
元件建置。
只要進行搜尋,ComboboxInput
便會自動開啟/關閉 ComboboxOptions
。
完全由您決定要如何篩選結果,不論是使用模糊搜尋函式庫於用戶端處理,還是向 API 進行伺服器端要求。在此範例中,我們只是為了展示目的而保持邏輯簡潔。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person" :value="person" > {{ person }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ 'Durward Reynolds', 'Kenton Towne', 'Therese Wunsch', 'Benedict Kessler', 'Katelyn Rohan', ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
在前面的範例中,我們使用清單中的 字串
值作為資料,但也可以使用有附加資訊的物件。唯一的限制就是您必須向 input 提供 displayValue
。這一點很重要,以便您物件的字串版本能顯示在 ComboboxInput
中。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value"
:displayValue="(person) => person.name"/> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" :disabled="person.unavailable" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds', unavailable: false }, { id: 2, name: 'Kenton Towne', unavailable: false }, { id: 3, name: 'Therese Wunsch', unavailable: false }, { id: 4, name: 'Benedict Kessler', unavailable: true }, { id: 5, name: 'Katelyn Rohan', unavailable: false }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
Headless UI 會記錄每個元件的許多狀態,例如目前選取哪個組合方塊選項,是打開或關閉彈出視窗,以及哪個組合方塊項目目前以鍵盤啟用。
但由於這些元件在未套用樣式之前都是沒有標題和樣式的,因此在你提供自己想要的每個狀態的樣式之前,在你的使用者介面中無法看到這些資訊。
每個元件都透過供你用來有條件套用不同樣式或呈現不同內容的 插槽道具 公開其目前狀態的資訊。
例如:ComboboxOption
元件公開一個 active
狀態,它會告訴你該項目目前是透過滑鼠或鍵盤取得焦點。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <!-- Use the `active` state to conditionally style the active option. --> <!-- Use the `selected` state to conditionally style the selected option. --> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" as="template"
v-slot="{ active, selected }"> <li :class="{'bg-blue-500 text-white': active,'bg-white text-black': !active,}" ><CheckIcon v-show="selected" />{{ person.name }} </li> </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' import { CheckIcon } from '@heroicons/vue/20/solid' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
如需所有可用插槽道具的完整清單,請參閱 元件 API 文件。
每個元件也透過你可以用來有條件套用不同樣式的 data-headlessui-state
屬性公開其目前狀態的資訊。
當 插槽道具 API 中的任何狀態為 true
時,它們會以空格分隔字串的形式列在此屬性中,以便你可以以 [attr~=value]
型式的 CSS 屬性選取器 來鎖定它們。
例如:下列是 ComboboxOptions
元件會在組合框開啟,且第二個項目處於 active
狀態時,連同某些子 ComboboxOption
元件一起呈現
<!-- Rendered `ComboboxOptions` --> <ul data-headlessui-state="open"> <li data-headlessui-state="">Wade Cooper</li> <li data-headlessui-state="active selected">Arlene Mccoy</li> <li data-headlessui-state="">Devon Webb</li> </ul>
如果你正在使用 Tailwind CSS,你可以使用 @headlessui/tailwindcss 外掛程式以使用 ui-open:*
和 ui-active:*
等修飾詞來鎖定此屬性
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person"
class="ui-active:bg-blue-500 ui-active:text-white ui-not-active:bg-white ui-not-active:text-black"><CheckIcon class="hidden ui-selected:block" />{{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' import { CheckIcon } from '@heroicons/vue/20/solid' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
與僅允許你提供字串作為值的原生 HTML 表單控制元件不同,Headless UI 也支援繫結複雜物件。
<template>
<Combobox v-model="selectedPerson"><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id":value="person":disabled="person.unavailable" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue'const people = [{ id: 1, name: 'Durward Reynolds', unavailable: false },{ id: 2, name: 'Kenton Towne', unavailable: false },{ id: 3, name: 'Therese Wunsch', unavailable: false },{ id: 4, name: 'Benedict Kessler', unavailable: true },{ id: 5, name: 'Katelyn Rohan', unavailable: false },]const selectedPerson = ref(people[1]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
在將物件繫結為值時,務必確保你將物件的相同執行個體用於 Combobox
的 value
和相對應的 ComboboxOption
,否則它們將無法相等,並導致組合框無法正確運作。
為了更輕鬆地處理相同物件的不同執行個體,你可以使用 by
道具,透過特定欄位來比較物件,而非比較物件識別。
<template> <Combobox :modelValue="modelValue" @update:modelValue="value => emit('update:modelValue', value)"
by="id"> <ComboboxInput @change="query = $event.target.value" :displayValue="(department) => department.name" /> <ComboboxOptions> <ComboboxOption v-for="department in filteredDepartments" :key="department.id" :value="department" > {{ department.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const props = defineProps({ modelValue: Object }) const emit = defineEmits(['update:modelValue']) const departments = [ { id: 1, name: 'Marketing', contact: 'Durward Reynolds' }, { id: 2, name: 'HR', contact: 'Kenton Towne' }, { id: 3, name: 'Sales', contact: 'Therese Wunsch' }, { id: 4, name: 'Finance', contact: 'Benedict Kessler' }, { id: 5, name: 'Customer service', contact: 'Katelyn Rohan' }, ] const query = ref('') const filteredDepartments = computed(() => query.value === '' ? departments : departments.filter((department) => { return department.name .toLowerCase() .includes(query.value.toLowerCase()) }) ) </script>
如果你想完全控制比較物件的方式,也可以將自己的比較函式傳遞給 by
道具
<template> <Combobox :modelValue="modelValue" @update:modelValue="value => emit('update:modelValue', value)"
:by="compareDepartments"> <ComboboxInput @change="query = $event.target.value" :displayValue="(department) => department.name" /> <ComboboxOptions> <ComboboxOption v-for="department in departments" :key="department.id" :value="department" > {{ department.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const props = defineProps({ modelValue: Object }) const emit = defineEmits(['update:modelValue'])function compareDepartments(a, b) {return a.name.toLowerCase() === b.name.toLowerCase()}const departments = [ { id: 1, name: 'Marketing', contact: 'Durward Reynolds' }, { id: 2, name: 'HR', contact: 'Kenton Towne' }, { id: 3, name: 'Sales', contact: 'Therese Wunsch' }, { id: 4, name: 'Finance', contact: 'Benedict Kessler' }, { id: 5, name: 'Customer service', contact: 'Katelyn Rohan' }, ] const query = ref('') const filteredDepartments = computed(() => query.value === '' ? departments : departments.filter((department) => { return department.name .toLowerCase() .includes(query.value.toLowerCase()) }) ) </script>
Combobox 元件允許您選擇多個值。您可以透過提供值的陣列(而非單一值)來啟用此功能。
<template>
<Combobox v-model="selectedPeople" multiple><ul v-if="selectedPeople.length > 0"> <li v-for="person in selectedPeople" :key="person.id"> {{ person.name }} </li> </ul> <ComboboxInput /> <ComboboxOptions> <ComboboxOption v-for="person in people" :key="person.id" :value="person"> {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPeople = ref([people[0], people[1]]) </script>
這會在您選擇選項時保持組合框開啟,選擇選項也會在該位置切換。
只要加入或移除選項,您的 v-model
繫結就會更新為包含所有選取選項的陣列。
預設情況下,Combobox
會使用輸入內容作為螢幕閱讀器的標籤。如果您想進一步控制協助技術所宣告的內容,請使用 ComboboxLabel
元件。
<template> <Combobox v-model="selectedPerson">
<ComboboxLabel>Assignee:</ComboboxLabel><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxLabel, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
如果您為組合框加入 name
屬性,將會產生隱藏的 input
元素,並與您的選定值保持同步。
<template> <form action="/projects/1/assignee" method="post">
<Combobox v-model="selectedPerson" name="assignee"><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> <button>Submit</button> </form> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
這樣便能讓您在原生 HTML <form>
內部使用組合框,並進行傳統的表單提交,就好像您的組合框是原生的 HTML 表單控制項一樣。
字串等基本值會呈現為包含該值的單一隱藏輸入,但像物件等複雜的值會使用名稱的方括號表示法編碼為多個輸入。
<input type="hidden" name="assignee[id]" value="1" /> <input type="hidden" name="assignee[name]" value="Durward Reynolds" />
如果您提供 defaultValue
而非 value
屬性給 Combobox
,Headless UI 會為您在內部追蹤其狀態,讓您可以將其作為 非受控元件 使用。
<template> <form action="/projects/1/assignee" method="post">
<Combobox name="assignee" :defaultValue="people[0]"><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> <button>Submit</button> </form> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
這可以在組合框 與 HTML 表單 或使用 FormData 而非使用 React 狀態追蹤其狀態的表單 API 搭配使用時簡化您的程式碼。
如果您在元件值變更時需要執行任何副作用,您提供的任何 @update:modelValue
屬性仍會被呼叫,但您不需要使用它自行追蹤元件的狀態。
您可以透過根據 query
值加入動態的 ComboboxOption
,允許使用者輸入清單中不存在的自身值。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions>
<ComboboxOption v-if="queryPerson" :value="queryPerson">Create "{{ query }}"</ComboboxOption><ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('')const queryPerson = computed(() => {return query.value === '' ? null : { id: null, name: query.value }})const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
根據您建立的項目,有時在 <ComboboxOptions>
外部渲染關於目前選取選項的附加資訊可能會比較有意義。例如,在命令面板內容中預覽目前的選取選項。在這種情況下,您可以讀取 `activeOption` 插槽 prop 參數存取這些資訊。
<template>
<Combobox v-model="selectedPerson" v-slot="{ activeOption }"><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions><div v-if="activeOption">The current active user is: {{ activeOption.name }}</div></Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
目前 `activeOption` 會是目前 `ComboboxOption` 的 `value`。
預設情況下,您的 ComboboxOptions
實例會根據 Combobox
組合元件本身內部追蹤的內部 `open` 狀態自動顯示/隱藏。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <!-- By default, the `ComboboxOptions` will automatically show/hide when typing in the `ComboboxInput`, or when pressing the `ComboboxButton`. --> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
如果您想要自行處理(可能是因為某種原因需要額外加上一個包覆元素),您可以將 `static` prop 加入到 ComboboxOptions
實例,指示它總是渲染,並檢查 Combobox
提供的 `open` 插槽 prop 以控制您顯示/隱藏的元素。
<template>
<Combobox v-model="selectedPerson" v-slot="{ open }"><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /><div v-show="open"><!-- Using the `static` prop, the `ComboboxOptions` are always rendered and the `open` state is ignored. --><ComboboxOptions static><ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </div> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
使用 `disabled` prop 停用 ComboboxOption
。此動作會透過滑鼠和鍵盤讓其無法選取,且在按下上/下箭頭時也會略過它。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <ComboboxOptions> <!-- Disabled options will be skipped by keyboard navigation. --> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person"
:disabled="person.unavailable"> <span :class='{ "opacity-75": person.unavailable }'> {{ person.name }} </span> </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds', unavailable: true }, { id: 2, name: 'Kenton Towne', unavailable: false }, { id: 3, name: 'Therese Wunsch', unavailable: false }, { id: 4, name: 'Benedict Kessler', unavailable: true }, { id: 5, name: 'Katelyn Rohan', unavailable: false }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
預設情況下,一旦您在組合方塊選取一個值,就無法將組合方塊清除回空白值,當您清除輸入並離開標籤頁時,值會變回之前選取的值。
如果您想要在您的組合方塊中支援空白值,請使用 nullable
prop。
<template>
<Combobox v-model="selectedPerson" nullable><ComboboxInput @change="query = $event.target.value":displayValue="(person) => person?.name"/> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds', unavailable: true }, { id: 2, name: 'Kenton Towne', unavailable: false }, { id: 3, name: 'Therese Wunsch', unavailable: false }, { id: 4, name: 'Benedict Kessler', unavailable: true }, { id: 5, name: 'Katelyn Rohan', unavailable: false }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
當使用 nullable
prop 時,清除輸入並離開元素將會更新您的 v-model
繫結,並使用 null
呼叫您的 displayValue
回呼。
允許 選取多個值 時,此 prop 不會進行任何操作,因為選項會切換開啟和關閉,如果沒有選擇任何選項,則會產生空陣列 (而不是 null)。
若要對組合方塊的開啟/關閉進行動畫,你可以使用 Vue 內建的 <transition>
組件。你所要做的就是將你的 ComboboxOptions
執行個體包裝在一個 <transition>
中,然後會自動套用轉場。
<template> <Combobox v-model="selectedPerson"> <ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <!-- Use Vue's built-in `transition` component to add transitions. -->
<transitionenter-active-class="transition duration-100 ease-out"enter-from-class="transform scale-95 opacity-0"enter-to-class="transform scale-100 opacity-100"leave-active-class="transition duration-75 ease-out"leave-from-class="transform scale-100 opacity-100"leave-to-class="transform scale-95 opacity-0"><ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions></transition></Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
如果你要為組合方塊的相異子項協調多個轉場,請查看 Headless UI 中包含的轉場組件。
預設情況下,Combobox
及其子組件各會呈現對於該組件而言明智的預設元素。
例如,ComboboxLabel
預設會呈現一個 label
,ComboboxInput
會呈現一個 input
,ComboboxButton
會呈現一個 button
,ComboboxOptions
會呈現一個 ul
,而 ComboboxOption
則是會呈現一個 li
。與之相反,Combobox
並不會呈現一個元素,而是直接呈現其子項。
這很容易透過 as
屬性進行更改,它存在於每個組件。
<template> <!-- Render a `div` instead of nothing -->
<Combobox as="div" v-model="selectedPerson"><ComboboxInput @change="query = $event.target.value" :displayValue="(person) => person.name" /> <!-- Render a `div` instead of a `ul` --><ComboboxOptions as="div"><!-- Render a `span` instead of a `li` --> <ComboboxOptionas="span"v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
若要告訴元素以沒有包裝的元素直接呈現其子項,請使用 as="template"
。
<template> <Combobox v-model="selectedPerson"> <!-- Render children directly instead of an `input` --> <ComboboxInput
as="template"@change="query = $event.target.value" :displayValue="(person) => person.name" > <input /> </ComboboxInput> <ComboboxOptions> <ComboboxOption v-for="person in filteredPeople" :key="person.id" :value="person" > {{ person.name }} </ComboboxOption> </ComboboxOptions> </Combobox> </template> <script setup> import { ref, computed } from 'vue' import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from '@headlessui/vue' const people = [ { id: 1, name: 'Durward Reynolds' }, { id: 2, name: 'Kenton Towne' }, { id: 3, name: 'Therese Wunsch' }, { id: 4, name: 'Benedict Kessler' }, { id: 5, name: 'Katelyn Rohan' }, ] const selectedPerson = ref(people[0]) const query = ref('') const filteredPeople = computed(() => query.value === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.value.toLowerCase()) }) ) </script>
當組合方塊被切換為開啟時,ComboboxInput
會保持焦點。
對於預設的標籤流,ComboboxButton
會被忽略,這表示在 ComboboxInput
中按 Tab
將會略過 ComboboxButton
。
按一下 ComboboxButton
會切換選項清單開啟和關閉。按一下選項清單以外的任何地方都會關閉組合方塊。
指令 | 說明 |
ArrowDown, 或ArrowUp當 | 開啟下拉式選單並讓焦點在選定的項目上 |
Enter, Space, ArrowDown, or ArrowUp當 | 開啟下拉式選單,讓焦點在輸入欄位並選取選定項目 |
Esc 當下拉式選單開啟時 | 關閉下拉式選單並在輸入欄位中復原選定項目 |
ArrowDown or ArrowUp當下拉式選單開啟時 | 讓焦點在先前的/下一個非停用的項目 |
Home or PageUp 當下拉式選單開啟時 | 讓焦點在第一個非停用的項目 |
End or PageDown 當下拉式選單開啟時 | 讓焦點在最後一個非停用的項目 |
Enter 當下拉式選單開啟時 | 選取目前的項目 |
Enter 當下拉式選單關閉且在表單中時 | 提交表單 |
Tab 當下拉式選單開啟時 | 選取目前已啟用的項目並關閉下拉式選單 |
A–Z 或 a–z 開啟選單時 | 呼叫 |
屬性 | 預設值 | 說明 |
as | 範本 | 字串 | 元件
|
v-model | — | T 選取的值。 |
defaultValue | — | T 當使用非受控元件時預設值。 |
by | — | keyof T | ((a: T, z: T) => boolean) 透過此屬性,可以根據特定欄位比較物件,或傳入自訂比較函式來完全控制物件的比較方式。 |
disabled | false | 布林值 使用此屬性停用整個 combobox 元件及其關聯子代。 |
name | — | 字串 在表單中使用此元件時的名稱。 |
nullable | — | 布林值 是否可以清除 combobox。 |
multiple | false | 布林值 是否允許多個選項。 |
插槽屬性 | 說明 |
value |
選取的值。 |
open |
combobox 是否開啟。 |
disabled |
combobox 是否已停用。 |
activeIndex |
目前選項的索引,或為 null。 |
activeOption |
目前選項,或為 null。 |
屬性 | 預設值 | 說明 |
as | input | 字串 | 元件
|
displayValue | — | (item: T) => 字串 你的 |
渲染屬性 | 說明 |
open |
Combobox 是否開啟。 |
disabled |
Combobox 是否已停用。 |
屬性 | 預設值 | 說明 |
as | button | 字串 | 元件
|
插槽屬性 | 說明 |
value |
選取的值。 |
open |
Combobox 是否開啟。 |
disabled |
Combobox 是否已停用。 |
屬性 | 預設值 | 說明 |
as | 標籤 | 字串 | 元件
|
插槽屬性 | 說明 |
open |
Combobox 是否開啟。 |
disabled |
Combobox 是否已停用。 |
屬性 | 預設值 | 說明 |
as | ul | 字串 | 元件
|
static | false | 布林值 元素是否應忽略內部管理的開啟/關閉狀態。 |
unmount | true | 布林值 元素是否應基於開啟/關閉狀態解除安裝或隱藏。 |
hold | false | 布林 當滑鼠離開活躍選項時,活躍選項是否應保持活躍狀態。 |
插槽屬性 | 說明 |
open |
Combobox 是否開啟。 |
屬性 | 預設值 | 說明 |
value | — | T 選項值。 |
as | li | 字串 | 元件
|
disabled | false | 布林值 對於鍵盤導覽和 ARIA 目的,選項是否應禁用。 |
插槽屬性 | 說明 |
active |
選項是否為活躍/焦點選項。 |
selected |
選項是否為已選取選項。 |
disabled |
對於鍵盤導覽和 ARIA 目的,選項是否禁用。 |