Autocomplete
@cloudflare/kumo
import { Autocomplete } from "@cloudflare/kumo";

/** Basic autocomplete with a flat list of strings. */
export function AutocompleteDemo() {
  return (
    <Autocomplete items={fruits}>
      <Autocomplete.InputGroup placeholder="Search fruits…" />
      <Autocomplete.Content>
        <Autocomplete.List>
          {(item: string) => (
            <Autocomplete.Item key={item} value={item}>
              {item}
            </Autocomplete.Item>
          )}
        </Autocomplete.List>
      </Autocomplete.Content>
    </Autocomplete>
  );
}

安装

批量导入

import { Autocomplete } from "@cloudflare/kumo";

按需导入

import { Autocomplete } from "@cloudflare/kumo/components/autocomplete";

适用场景

当输入值可以是自由文本、建议仅为可选提示时,使用 Autocomplete。当选中的值必须来自预定义列表时,改用 Combobox

受控

传入 valueonValueChange 以实现受控用法。

import { useState } from "react";
import { Autocomplete } from "@cloudflare/kumo";

/** Controlled autocomplete with value and onValueChange. */
export function AutocompleteControlledDemo() {
  const [value, setValue] = useState("");

  return (
    <div className="flex w-80 flex-col gap-3">
      <Autocomplete
        items={fruits}
        value={value}
        onValueChange={(v) => setValue(v)}
      >
        <Autocomplete.InputGroup placeholder="Type a fruit…" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: string) => (
              <Autocomplete.Item key={item} value={item}>
                {item}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
      {value && (
        <p className="text-sm text-kumo-subtle">
          Value: <span className="font-medium text-kumo-default">{value}</span>
        </p>
      )}
    </div>
  );
}

配合 Field

添加 labeldescriptionrequired 以启用内置的 Field 包装器。

Start typing to filter languages

import { useCallback } from "react";
import { Autocomplete } from "@cloudflare/kumo";
import { languages, Language } from "./data/languages";

/** Autocomplete with label, description, and Field wrapper. */
export function AutocompleteWithFieldDemo() {
  const { contains } = Autocomplete.useFilter();

  const filter = useCallback(
    (item: Language, query: string) => contains(item.label, query),
    [contains],
  );

  return (
    <div className="w-80">
      <Autocomplete
        items={languages}
        label="Language"
        description="Start typing to filter languages"
        filter={filter}
      >
        <Autocomplete.InputGroup placeholder="Search a language…" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: Language) => (
              <Autocomplete.Item key={item.value} value={item}>
                {item.emoji} {item.label}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
    </div>
  );
}

错误状态

通过 error 属性展示校验错误。

Please enter a valid country
import { useCallback } from "react";
import { Autocomplete } from "@cloudflare/kumo";

/** Autocomplete with error state via the Field wrapper. */
export function AutocompleteErrorDemo() {
  const { contains } = Autocomplete.useFilter();

  const filter = useCallback(
    (item: Country, query: string) => contains(item.label, query),
    [contains],
  );

  return (
    <div className="w-80">
      <Autocomplete
        items={countries}
        label="Country"
        error={{ message: "Please enter a valid country", match: true }}
        filter={filter}
      >
        <Autocomplete.InputGroup placeholder="Search countries…" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: Country) => (
              <Autocomplete.Item key={item.code} value={item}>
                {item.label}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
    </div>
  );
}

分组

使用 Autocomplete.GroupAutocomplete.GroupLabel 将项目按类别分组。

import { Autocomplete } from "@cloudflare/kumo";

/** Autocomplete with grouped items using Group and GroupLabel. */
export function AutocompleteGroupedDemo() {
  return (
    <Autocomplete items={servers}>
      <Autocomplete.InputGroup placeholder="Select region…" />
      <Autocomplete.Content>
        <Autocomplete.List>
          {(group: ServerGroup) => (
            <Autocomplete.Group key={group.value} items={group.items}>
              <Autocomplete.GroupLabel>{group.value}</Autocomplete.GroupLabel>
              <Autocomplete.Collection>
                {(item: ServerLocation) => (
                  <Autocomplete.Item key={item.value} value={item}>
                    {item.label}
                  </Autocomplete.Item>
                )}
              </Autocomplete.Collection>
            </Autocomplete.Group>
          )}
        </Autocomplete.List>
      </Autocomplete.Content>
    </Autocomplete>
  );
}

尺寸

Autocomplete.InputGroup 上的 size 属性支持与 Input 组件一致的四种变体:xssmbase(默认)和 lg

import { Autocomplete } from "@cloudflare/kumo";

/** Demonstrates the four size variants: xs, sm, base, and lg. */
export function AutocompleteSizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Autocomplete items={fruits.slice(0, 10)}>
        <Autocomplete.InputGroup size="xs" placeholder="xs" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: string) => (
              <Autocomplete.Item key={item} value={item}>
                {item}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
      <Autocomplete items={fruits.slice(0, 10)}>
        <Autocomplete.InputGroup size="sm" placeholder="sm" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: string) => (
              <Autocomplete.Item key={item} value={item}>
                {item}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
      <Autocomplete items={fruits.slice(0, 10)}>
        <Autocomplete.InputGroup size="base" placeholder="base (default)" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: string) => (
              <Autocomplete.Item key={item} value={item}>
                {item}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
      <Autocomplete items={fruits.slice(0, 10)}>
        <Autocomplete.InputGroup size="lg" placeholder="lg" />
        <Autocomplete.Content>
          <Autocomplete.List>
            {(item: string) => (
              <Autocomplete.Item key={item} value={item}>
                {item}
              </Autocomplete.Item>
            )}
          </Autocomplete.List>
        </Autocomplete.Content>
      </Autocomplete>
    </div>
  );
}

过滤

默认情况下过滤不区分大小写和重音符号,底层由 Intl.Collator 实现。对于字符串项,无需自定义 filter

当需要对对象项的某个属性进行过滤时,使用 Autocomplete.useFilter() 以保留内置的不区分重音符号的匹配行为:

function LanguagePicker() {
  const { contains } = Autocomplete.useFilter();

  const filter = useCallback(
    (item: Language, query: string) => contains(item.label, query),
    [contains],
  );

  return (
    <Autocomplete items={languages} filter={filter}>
      {/* ... */}
    </Autocomplete>
  );
}

若要完全禁用过滤(例如结果来自服务器时),传入 filter={null}

<Autocomplete items={results} filter={null}>
  ...
</Autocomplete>

API 参考

Autocomplete

根组件。包装所有子组件并管理状态。

PropTypeDefaultDescription
items*unknown[]-Array of items to display in the dropdown
valuestring | number | string[]-The controlled input value
openboolean-Whether the popup is open (controlled)
childrenReactNode-Autocomplete content (input group, popup content)
classNamestring-Additional CSS classes
labelReactNode-Label content (enables Field wrapper)
requiredboolean-Whether the field is required
labelTooltipReactNode-Tooltip content to display next to the label
descriptionReactNode-Helper text displayed below the field
errorstring | object-Error message or validation error object

Autocomplete.InputGroup

自包含的输入包装器,将文本输入框、清除按钮和下拉触发器渲染在一起。

PropTypeDefault
classNamestring-
sizeKumoAutocompleteSize-
placeholderstring-

Autocomplete.Content

下拉弹层容器。包装 Portal、Positioner 和 Popup。

PropTypeDefault
childrenReactNode-
classNamestring-
alignAutocompleteBase.Positioner.Props["align"]-
alignOffsetAutocompleteBase.Positioner.Props["alignOffset"]-
sideAutocompleteBase.Positioner.Props["side"]-
sideOffsetAutocompleteBase.Positioner.Props["sideOffset"]-

Autocomplete.Item

列表中的单个建议项。

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

其他子组件

  • Autocomplete.List — 带 render prop 的可滚动列表容器
  • Autocomplete.Group — 将项目归并到某个标题下
  • Autocomplete.GroupLabel — 分组的标题标签
  • Autocomplete.Collection — 组内的项目容器
  • Autocomplete.Separator — 项目之间的水平分隔线