# 联想搜索 · Typeahead

- 页面: https://jiguang132.com/zh/resources/astryx/docs/typeahead
- 资源: Astryx (https://jiguang132.com/zh/resources/astryx.md)
- 全部指南: https://jiguang132.com/zh/resources/astryx/docs.md

根据输入实时筛选建议项，并通过键盘确认选择。

## 安装与前置条件

```bash
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
```

需要 React 19+，按顺序导入基础 CSS 和主题，并设置 Theme Provider。参见[开始使用](/resources/astryx/docs/getting-started)。

## 示例与使用方式

- **联想搜索 · 自定义 / 搜索 1**：首先体验这个示例，再查看代码理解组件的组合方式。
- **联想搜索 · 综合展示 2**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 综合展示 3**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 综合展示 4**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 基础用法 5**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 交互示例 6**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 搜索 7**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 交互示例 8**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **联想搜索 · 输入校验 9**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。

## API 与上游详细参考

优先使用 0.7.1 官方中文说明；上游尚未提供中文的条目保留英文。属性名、类型及默认值保持原生 API，不把示例状态名称当成组件属性。

## Base Typeahead

Composable combobox engine providing a bare input, search, keyboard navigation, and a styled result dropdown. It renders no input wrapper, border, or selected-value token. Typeahead and Tokenizer compose it for standard fields.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| searchSource * | SearchSource<T> |  | 提供搜索和引导方法的数据源。 |
| value * | T \| null |  | 当前选中的项目。 |
| onChange * | (item: T \| null) => void |  | 选择变更时调用。 |
| renderItem | (item: T) => ReactNode |  | 下拉列表项的自定义渲染函数。 |
| placeholder | string | 'Search…' | 输入框占位文本。 |
| hasEntriesOnFocus | boolean | false | 聚焦时在输入前显示引导结果。 |
| maxMenuItems | number | 10 | 下拉列表显示的最大项目数。 |
| menuWidth | number |  | 视口限制前请求的下拉菜单像素宽度。 |
| minQueryLength | number | 1 | 查询搜索源前的最小查询长度。低于该长度不会发起搜索，菜单保持关闭。 |
| emptySearchText | ReactNode | 'No results found' | 查询无匹配结果时显示的内容。接受 ReactNode，因此可在无结果处放置链接或创建入口。会从 DOM 读取其渲染出的文本在礼貌性实时区域中播报；aria-hidden 的部分两处都不包含，不渲染任何文本的内容则不会播报。null 与 undefined 同样视为未提供，将回退到默认值；若要不渲染任何内容，请传入空字符串。 |
| isDisabled | boolean | false | 输入框是否被禁用。 |
| isFocusableDisabled | boolean | false | 使用 aria-disabled 和只读状态保持禁用输入框可聚焦，以便访问调用方提供的禁用原因。它会阻止文本输入，但如果结果已打开，按 Enter 仍可选择高亮选项。 |
| hasAutoFocus | boolean | false | 挂载时自动聚焦输入框。 |
| debounceMs | number | 150 | 触发搜索前的防抖延迟（毫秒）。同步数据源设置为 0。 |
| size | 'sm' \| 'md' \| 'lg' | 'md' | 用于调整下拉选项内边距的尺寸。 |
| anchorRef | RefObject<HTMLElement \| null> |  | 用于下拉列表定位的锚点元素引用。未提供时使用输入框本身。 |
| inputXStyle | StyleXStyles |  | 输入元素的附加 StyleX 样式。 |
| xstyle | StyleXStyles |  | 应用于输入元素的标准 BaseProps StyleX 样式。 |
| inputTabIndex | number |  | 原生 tabIndex 的旧输入专用别名。提供时优先；未提供时保留原生属性。 |
| onKeyDown | (e: React.KeyboardEvent<HTMLInputElement>) => void |  | 在内部键盘导航之前调用的附加 keydown 处理函数。调用 e.preventDefault() 可跳过内部处理。 |
| onChangeQuery | (query: string) => void |  | 搜索查询文本变更时触发的回调。 |
| onOpenChange | (isOpen: boolean) => void |  | 下拉列表打开或关闭时的回调。 |
| inputId | string |  | 原生 id 的旧输入专用别名。提供时优先；未提供时保留原生属性。 |
| ariaDescribedBy | string |  | 原生 aria-describedby 的旧输入专用别名。提供时优先；未提供时保留原生属性。 |
| ariaLabelledBy | string |  | 原生 aria-labelledby 的旧输入专用别名。提供时优先；未提供时保留原生属性。 |

### 使用建议

- Use Typeahead or Tokenizer for standard fields; they wrap BaseTypeahead with input chrome and selected-value rendering it intentionally omits.
- Provide your own visible label or aria-label and custom input wrapper so the bare combobox has an accessible name, focus treatment, border, and layout.
- Pass anchorRef pointing to your wrapper so the dropdown positions against your custom input chrome, not just the bare input element.
- 避免：Expect input chrome or selected-value rendering. BaseTypeahead is an engine; the caller owns those visible parts.
- 避免：Use BaseTypeahead when Typeahead or Tokenizer would suffice; the extra wrapper and styling work is only justified for truly custom compositions.
- 避免：Treat Escape as cancellation of pending source work. It hides the current popup, but a late response can reopen it.

### 组件结构

- **Input**: Bare combobox input. The caller supplies its visible field chrome and accessible name.
- **Loading status**: Named Spinner shown beside the input while an asynchronous source is pending, unless a composed owner takes over the busy indicator lane.
- **Dropdown**: Anchored listbox surface containing current search or bootstrap results.
- **Empty state**: Disabled listbox option shown after a completed search returns no results.
- **Result group heading**: Visible label for a group of result options.
- **Result row**: Option wrapper that owns highlight, selection, pointer, and keyboard behavior.
- **Default item content**: TypeaheadItem label and optional supporting content rendered inside a result row.
- **Caller-rendered item content**: Caller content supplied through renderItem or item.element inside the stable result row.
- **Selected result state**: Selected row weight and trailing check shown when a result matches value.

## Typeahead

A searchable input for selecting a single item from a large or dynamic dataset. Results appear as the user types, with support for async data sources, debounced search, and custom item rendering. Use it when the option list is too large for a Selector dropdown.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label * | string |  | Accessible label for the input. |
| searchSource * | SearchSource<T> |  | Data source providing search and bootstrap methods for populating the dropdown. |
| value * | T \| null |  | Currently selected item, or null if nothing is selected. |
| onChange * | (item: T \| null) => void |  | Called when the selection changes. |
| placeholder | string |  | Input placeholder text. |
| hasEntriesOnFocus | boolean | false | Show bootstrap results on focus before typing. |
| hasClear | boolean | true | Show clear button to deselect the current value. |
| isDisabled | boolean | false | Disables the input. |
| disabledMessage | string |  | Explains why the input is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the field focusable via aria-disabled (activation stays blocked). Use this instead of wrapping a disabled Typeahead in Tooltip. Disabled controls swallow the hover events an external Tooltip needs. |
| maxMenuItems | number | 10 | Maximum number of dropdown items to display. |
| minQueryLength | number | 1 | Minimum query length before the search source is queried. Below it no search runs and the menu stays closed. |
| status | {type: 'warning' \| 'error' \| 'success', message?: string} |  | Validation status object with type and message for error/warning/success states. |
| statusVariant | 'attached' \| 'detached' \| 'tooltip' | 'attached' | How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing; tooltip reveals the message in an info-tip button. |
| renderItem | (item: T) => ReactNode |  | Custom render function for dropdown items. Default renders TypeaheadItem. |
| isLabelHidden | boolean | false | Visually hides the label while keeping it accessible. |
| description | string |  | Helper text displayed below the label. |
| isRequired | boolean | false | Marks the field as required. |
| isOptional | boolean | false | Shows an optional indicator on the label. |
| labelTooltip | string |  | Tooltip text shown on the label. |
| emptySearchText | ReactNode | 'No results found' | Content shown when the query matched nothing. Takes a ReactNode, so the dead end can carry a link or a create row. Announced in a polite live region as the text it renders, read from the DOM; aria-hidden parts stay out of both, and content that renders no text announces nothing. null counts as not given, like undefined, and falls through to the default; pass an empty string to render nothing. |
| hasAutoFocus | boolean | false | Auto-focus the input on mount. |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Input and token size. |
| debounceMs | number | 150 | Debounce delay in ms before triggering search. Set to 0 for synchronous sources. |
| onChangeQuery | (query: string) => void |  | Callback fired when the search query text changes. |
| onOpenChange | (isOpen: boolean) => void |  | Callback when the dropdown opens or closes. |
| width | SizeValue |  | Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned. |
| startIcon | IconType \| ReactNode |  | SVG icon component displayed at the start of the input. |
| containerXstyle | StyleXStyles |  | StyleX styles for the field container (label, control, and status together). No effect inside InputGroup (style the InputGroup instead) or in a `horizontal-labels` FormLayout. Root `xstyle`, `className`, and `style` are not accepted. |
| containerClassName | string |  | Class name for the field container. Same targeting as `containerXstyle`. |
| containerStyle | CSSProperties |  | Inline styles for the field container. Same targeting as `containerXstyle`. |
| controlXstyle | StyleXStyles |  | StyleX styles for the visual control box: the element that paints the border and background and holds icons and end controls. Never the native input. |
| controlClassName | string |  | Class name for the visual control box. |
| controlStyle | CSSProperties |  | Inline styles for the visual control box. |

### 使用建议

- Provide descriptive placeholder text that hints at what users can search for.
- Show suggestions on focus when users benefit from seeing popular or recent options before typing.
- Add a search delay for remote data sources to avoid excessive network requests.
- 避免：Use for short, static option lists; use Selector for better discoverability.
- 避免：Use for multi-selection; use Tokenizer instead.
- 避免：Place multiple Typeaheads adjacent to each other without clear labels differentiating them.
- 避免：Wrap a disabled Typeahead in Tooltip to explain why it is disabled; disabled triggers swallow the hover events the wrapper needs. Use the disabledMessage prop instead.

### 主题 API

```json
{
  "targets": [
    {
      "className": "astryx-typeahead",
      "visualProps": [
        "status",
        "size"
      ]
    },
    {
      "className": "astryx-typeahead-dropdown"
    },
    {
      "className": "astryx-typeahead-empty-state"
    },
    {
      "className": "astryx-typeahead-item"
    }
  ]
}
```

## BaseTypeahead



## TypeaheadItem



### 组件结构

- **Field**: Standalone Field shell that provides the label and optional supporting content; omitted inside InputGroup.
- **Input surface**: Painted control surface containing the editable input or the selected token.
- **Icon-rendered start icon**: Optional leading semantic icon or icon component rendered through Icon.
- **Caller-rendered start content**: Optional arbitrary React content rendered directly at the start of the input surface.
- **Selected token**: Token that presents the selected item while the control is not in edit mode.
- **Spinner**: Loading indicator shown at the end of the input surface while a search is in flight.
- **Clear button**: Shared clear action that removes the selected item when hasClear is enabled.
- **Dropdown**: Anchored listbox surface containing the current search results.
- **Empty state**: Message shown after a completed search returns no results.
- **Result row**: Stable option wrapper that owns highlight, selection, pointer, and keyboard behavior.
- **Default item content**: Standard TypeaheadItem label and supporting content rendered inside a result row when renderItem and item.element are absent.
- **Caller-rendered item content**: Caller-owned result content supplied through renderItem or item.element inside the stable result row.
- **Result group heading**: Visible heading for a labeled group of result rows.
- **Selected result state**: Selected styling and trailing check presented on the current result row.

## Typeahead Item

预输入结果的默认下拉项渲染器。显示标签以及可选的图标、描述和头像。导出供自定义 renderItem 实现使用。

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| item * | SearchableItem |  | 要渲染的搜索结果项。 |
| icon | ReactNode |  | 在标签前显示的图标或头像。 |
| description | string |  | 显示在标签下方的描述文本。 |
| isDisabled | boolean | false | 此项是否在视觉上被禁用。 |
| group | string |  | 用于视觉分组的分组标签。 |


## 完整示例的额外依赖

部分示例使用独立图标包。示例清单中的每条安装命令包含该示例实际需要的依赖；若要运行本页全部示例，还需安装：

```bash
npm install @heroicons/react@2.2.0
```

## 示例的 StyleX 编译要求

BaseTypeaheadCustomSearch、BaseTypeaheadShowcase 包含 stylex.create，需要配置 StyleX 源码编译流程。仅引入 Core 预编译 CSS 不足以运行这些示例样式，复制前请阅读[样式与 Tailwind](/resources/astryx/docs/styling)。

---

JGuang 全部资源索引: https://jiguang132.com/llms.txt
