輸入元件
可搜尋選擇器
輸入即篩選的選擇器:注音組字中不過濾也不誤選,清單錨定並在邊緣翻轉,高亮列滑動,多選時選項化成可移除的標籤。
選項一多,下拉選單就該能打字找。這個 Combobox 把繁中輸入當成第一優先:用注音打「ㄊㄞˊ」時清單維持原樣、不跟著半成品亂跳,選完字才篩選;組字中按 Enter、方向鍵、Backspace 都交給輸入法,不會誤選或誤刪。預設比對把「臺」與「台」視為同一字,並可用 keywords 補英文名或別名。清單錨定在輸入框下方、下方空間不足時翻到上方,高亮列在選項間滑動;切成 multiple 後,選中的項目化成可移除的標籤,creatable 還能直接新增清單裡沒有的項目。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add "https://webberui.com/r/combobox.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
100
編輯中的呼叫預設屬性
<Combobox/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add "https://webberui.com/r/combobox.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/combobox 安裝。
使用
import { Combobox, type ComboboxOption } from "@/components/ui/combobox";
const cities: ComboboxOption[] = [
{ value: "taipei", label: "臺北市", group: "北部", keywords: ["Taipei"] },
{ value: "taichung", label: "臺中市", group: "中部", keywords: ["Taichung"] },
{ value: "kaohsiung", label: "高雄市", group: "南部", keywords: ["Kaohsiung"] },
];
// 單選:value 為 string | null
const [city, setCity] = React.useState<string | null>(null);
<Combobox options={cities} value={city} onValueChange={setCity} aria-label="配送縣市" />;
// 多選 + 可新增:value 為 string[]
const [tags, setTags] = React.useState<string[]>([]);
const [tagOptions, setTagOptions] = React.useState<ComboboxOption[]>([
{ value: "設計", label: "設計" },
{ value: "前端", label: "前端" },
]);
<Combobox
multiple
creatable
options={tagOptions}
value={tags}
onValueChange={setTags}
onCreate={(label) => setTagOptions((prev) => [...prev, { value: label, label }])}
maxVisibleChips={3}
aria-label="感興趣的主題"
/>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
options | ComboboxOption[] | — | 選項清單 |
multiple | boolean | false | 多選模式;決定 value 系列 props 的型別 |
value | string | null(多選為 string[]) | — | 受控的選取值;不提供時為非受控模式 |
defaultValue | string | null(多選為 string[]) | null/[] | 非受控模式的初始選取值 |
onValueChange | (value: string | null) => void(多選為 (value: string[]) => void) | — | 選取值變動時回呼 |
inputValue | string | — | 受控的輸入框文字 |
onInputValueChange | (value: string) => void | — | 輸入框文字變動時回呼(含組字中的暫存文字) |
filter | (option: ComboboxOption, query: string) => boolean | — | 自訂過濾;未提供時以 label 與 keywords 不分大小寫包含比對,並把「臺」視同「台」。查詢字為空時不呼叫 |
creatable | boolean | false | 查無完全相符的項目時,在清單底部顯示「新增「xxx」」列 |
onCreate | (label: string) => void | — | 選了新增列時回呼;新項目以 label 當 value 加入選取,請在這裡把它加進 options |
placeholder | string | "搜尋或選擇…" | 輸入框提示文字(多選且已有標籤時不顯示) |
emptyText | string | "找不到符合的項目" | 沒有任何符合項目時的文字 |
maxVisibleChips | number | — | 多選時最多顯示幾個標籤,其餘收成「+N」 |
open | boolean | — | 受控的清單開啟狀態 |
defaultOpen | boolean | false | 非受控模式的初始開啟狀態 |
onOpenChange | (open: boolean) => void | — | 清單開關時回呼 |
disabled | boolean | false | 停用整個元件 |
duration | number | 100 | 清單彈出動畫長度(毫秒),同時寫入 --wb-combobox-duration |
side | "bottom" | "top" | "bottom" | 清單偏好方向;空間不足時自動翻到另一側 |
sideOffset | number | 4 | 清單與輸入框的距離(px) |
id | string | — | 輸入框的 id,讓外部 label 以 htmlFor 指向 |
aria-label | string | — | 沒有可見標籤時的無障礙名稱 |
className | string | — | 透傳到最外層容器 |
ComboboxOption
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | string | — | 選項的值,在 options 內必須唯一 |
label | string | — | 顯示文字,也是預設比對的對象 |
group | string | — | 群組名稱;同名選項收在同一個群組標題下,依首次出現順序排列 |
disabled | boolean | false | 照常顯示但無法選取,鍵盤巡覽會跳過 |
keywords | string[] | — | 額外比對字,例如英文名「Taipei」或異體字 |
另外具名匯出 ComboboxOption、ComboboxSide、ComboboxProps、ComboboxSingleProps 與 ComboboxMultipleProps 型別。
細節
- 組字中不過濾:元件以
compositionstart/compositionend追蹤組字狀態。組字中輸入框照常顯示注音,但用來篩選的字凍結在組字前的值,選字完成後才以最終文字重新篩選——清單不會隨「ㄊㄞ」這類半成品閃動 - 組字中的按鍵交給輸入法:Enter、↑/↓、Backspace、Esc 都先檢查
isComposing與keyCode 229(Safari 在compositionend之後才送 keydown),組字中一律放行給輸入法選字,不會誤選、誤刪標籤 - 開關時機:點擊輸入框、開始打字、按 ↓ 或右側箭頭鈕會開啟;Esc 第一次關清單、第二次清空輸入框;點外或 Tab 離開會關閉。單選時選取後輸入框顯示該 label;失焦時若文字被改過但沒選,會還原為已選 label;在清單中再點一次已選項可取消選取
- 定位:清單以 portal 掛到
body、position: fixed,寬度等於輸入框外框(多選時含標籤列),下方空間不足時翻到上方;捲動與縮放視窗會以 rAF 節流重算位置,第一次量測前保持隱藏,不會閃在左上角 - 節奏:彈出為淡入 + 縮放 0.95 + 沿方向位移 8px,預設 100ms,由
duration控制;浮層內的 CSS 轉場讀--wb-combobox-duration,可在全域覆寫。高亮列以共用 layoutId 彈簧滑動;多選標籤以彈簧彈入,移除時其餘標籤以 FLIP 補位 - 新增:
creatable時,若輸入文字與現有 label 都不完全相同(同樣把「臺」視為「台」),清單底部出現新增列,按 Enter 或點擊即觸發onCreate
可及性
- 輸入框為
role="combobox",帶aria-expanded、aria-controls、aria-autocomplete="list",並以aria-activedescendant指向目前高亮的選項;焦點始終留在輸入框,點擊清單不會失焦 - 清單為
role="listbox"(多選時加aria-multiselectable),選項為role="option"並標示aria-selected、aria-disabled;群組以role="group"包住並由群組標題命名 - ↑/↓ 循環移動高亮並跳過停用項,Home/End 跳到首末項,Enter 選取;多選時輸入框為空按 Backspace 移除最後一個標籤
- 每個標籤的移除鈕都是可用 Tab 聚焦的按鈕,
aria-label為「移除 xxx」;結果數量由aria-live="polite"區域宣告(「共 N 個結果」或空狀態文字) - 使用者系統開啟「減少動態效果」時,清單只保留淡入(長度不變)、不縮放也不位移;高亮列與標籤直接到位,不使用彈簧與 FLIP 補位