# 选择器 · Selector

- 页面: https://jiguang132.com/zh/resources/astryx/docs/selector
- 资源: 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，不把示例状态名称当成组件属性。

## Selector

A dropdown selector for choosing a single value from a list of options. Supports labels, validation, descriptions, and required/optional states. Use it in forms and settings when presenting a moderate number of options. Keyboard typeahead matches a native select: typing on the focused closed trigger selects the matching option directly, repeated presses cycle through options sharing a first letter, and spaces count as match characters ("new y" reaches "New York"). With the menu open, typing moves the highlight and Enter commits. With hasSearch, typing on the closed trigger opens the popup and seeds the search input.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label * | string |  | Label text for accessibility. |
| options * | SelectorOption[] |  | Array of items: strings, objects with value/label/description/icon/disabled, dividers ({type: "divider"}), or sections ({type: "section", title, options}). |
| value | string |  | Currently selected value. |
| onChange | (value: string) => void |  | Callback fired when the selection changes. |
| hasClear | boolean | false | Shows a clear (×) button when a value is selected. When true, onChange also accepts null to signal the user cleared the selection. |
| hasSearch | boolean | false | Whether to show a search input for filtering options. As the user types, the match count (or "No results found") is announced to screen readers via a polite live region. The search field has built-in affordances: a leading magnifier icon and, once a query is typed, a trailing clear (✕) button that resets the query and returns focus to the input. |
| searchPlaceholder | string | 'Search...' | Placeholder text for the search input. |
| emptyText | ReactNode | 'No options' | Content shown in the dropdown panel when there are no options to show, and announced in a polite live region when the panel opens. The announcement is the text this content renders, read from the DOM, so an element is announced as written and aria-hidden parts are left out of both. Not shown while isLoading. |
| emptySearchText | ReactNode | 'No results found' | Content shown in the dropdown panel when a search query matches no options, and announced in a polite live region at the same time. The announcement is the text this content renders, read from the DOM, so an element is announced as written and aria-hidden parts are left out of both. |
| placeholder | string | 'Select...' | Placeholder text shown when no value is selected. |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Size variant for the selector. |
| variant | 'input' \| 'secondary' \| 'ghost' | 'input' | Visual trigger style. input is the bordered input treatment for forms; secondary matches the secondary buttons of the active theme (borderless and neutral-filled by default) for navs and toolbars; ghost is borderless and transparent, matching ghost buttons for toolbar usage. |
| isDisabled | boolean | false | Disables the selector. |
| isReadOnly | boolean | false | Makes the selector read-only: the selected value stays visible, focusable, and included in form submission, and retains its combobox identity with aria-readonly. The selection surface, clear action, and disclosure indicator are removed. Unlike isDisabled, the control is not dimmed. isDisabled takes precedence when both are set. |
| htmlName | string |  | The HTML name attribute for form submissions. Renders a hidden input carrying the selected value, like a native select. |
| disabledMessage | string |  | Explains why the selector is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the trigger focusable via aria-disabled (activation stays blocked). Use this instead of wrapping a disabled Selector in Tooltip. Disabled controls swallow the hover events an external Tooltip needs. |
| isLabelHidden | boolean | false | Visually hides the label while keeping it accessible. |
| description | string |  | Helper text displayed below the label. |
| isOptional | boolean | false | Marks the field as optional. |
| isRequired | boolean | false | Marks the field as required. |
| status | {type: 'error' \| 'warning' \| 'success', message?: string} |  | Validation status with an optional message. |
| statusVariant | 'attached' \| 'detached' \| 'tooltip' | 'attached' for input selectors; 'detached' for secondary and ghost selectors | How the status message is placed relative to the input. attached overlaps directly below the bordered input and is only valid for the input variant; secondary and ghost selectors detach attached status messages by default. Use tooltip for compact toolbar controls. |
| renderOption | (option: SelectorOptionData) => ReactNode |  | Custom render function for each selectable option in the dropdown. Use this instead of JSX children; dividers and sections are rendered by the selector. |
| renderValue | (option: SelectorOptionData) => ReactNode |  | Custom render function for the selected option inside the closed trigger. A one-line value follows the active size token, including compact or wide spacing scales with icons or a clear control. Padding accommodates the trigger line box, and each extra line grows the control by one line (28/32/36 for one line and 48/52/56 for two with the default tokens). Content inherits the trigger line height; content that sets a larger font should set its own line height so the trigger grows to fit it. Inside an InputGroup the group owns the row height: a SelectorOption folds onto one line and ellipsizes, and any taller node is cut off at the row. |
| indicatorPosition | 'start' \| 'end' | 'end' | Which logical edge of the option row carries a rendered selection mark. An empty mark consumes no space, so selected and unselected labels may shift or have different available width. end is the house convention shared with Typeahead and CommandPalette. |
| presentation | 'popover' \| 'bottom-sheet' \| 'adaptive' | 'popover' | How the option list is presented. adaptive uses a bottom sheet on compact touch screens and an anchored popover otherwise. |
| 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. Without a width the field follows its displayed value and can shrink with its row (the value truncates) instead of overflowing it; use `<HStack wrap="wrap">` for filter bars so selectors move to a second line on phones. |
| startIcon | IconType \| ReactNode |  | Icon displayed at the start of the selector trigger. |
| isLoading | boolean | false | Shows a loading spinner in the trigger. |
| onClick | (event: MouseEvent) => void |  | Click handler on the trigger button. Runs before the selector toggles its surface; preventDefault() does not keep it closed. |
| onFocus | (event: FocusEvent) => void |  | Focus handler on the trigger button. Runs before the selector's own focus handling. |
| onKeyDown | (event: KeyboardEvent) => void |  | Key handler on the trigger button. Runs after the built-in keyboard model and is skipped for keys it handles and cancels. |
| 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 the value, icons, and end controls. Never the trigger button itself. |
| controlClassName | string |  | Class name for the visual control box. |
| controlStyle | CSSProperties |  | Inline styles for the visual control box. |

### 使用建议

- Provide a visible label so users understand what they are selecting.
- Use sections and dividers to organize options when the list exceeds ~8 items.
- Use renderOption for custom option rows. Do not pass SelectorOption directly as JSX children.
- Set a meaningful placeholder that hints at the expected selection (e.g. "Choose a country" not "Select...").
- 避免：Use for action menus; use Dropdown Menu for triggering commands or navigation.
- 避免：Use when there are only two options; use a SegmentedControl or radio buttons instead.
- 避免：Use Selector for navigation; links should be links, not dropdown options.
- 避免：Use for yes/no or on/off choices; use Switch or CheckboxInput instead.
- 避免：Put more than ~20 options without sections; consider Typeahead for large lists.
- 避免：Wrap a disabled Selector 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-selector",
      "visualProps": [
        "variant",
        "size",
        "status"
      ],
      "states": [
        "disabled",
        "readonly"
      ]
    },
    {
      "className": "astryx-selector-option"
    },
    {
      "className": "astryx-selector-option-row",
      "visualProps": [
        "size"
      ],
      "states": [
        "selected",
        "disabled"
      ]
    },
    {
      "className": "astryx-selector-search"
    },
    {
      "className": "astryx-selector-section-heading"
    },
    {
      "className": "astryx-selector-empty-state"
    },
    {
      "className": "astryx-selector-indicator-icon",
      "states": [
        "state"
      ]
    },
    {
      "className": "astryx-selector-check"
    },
    {
      "className": "astryx-selector-popup"
    }
  ]
}
```

## SelectorOption



### 组件结构

- **Field**: Standalone Field shell that provides the label and optional supporting content; omitted inside InputGroup.
- **Trigger**: Painted control that displays the current selection or placeholder and opens the selection surface when editable.
- **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 trigger.
- **Trigger clear button**: Shared clear action that removes the selected value when hasClear is enabled.
- **Status icon**: Status glyph shown in place of the disclosure indicator for attached or tooltip status.
- **Indicator icon**: Trailing chevron shown when status presentation does not replace it; reflects collapsed or expanded state.
- **Search row**: Panel header with a borderless search input and optional clear action.
- **Search icon**: Leading magnifier rendered through Icon inside the search row.
- **Search clear button**: Shared clear action shown in the search row while a query is present.
- **Option row**: Selectable row for one supplied option.
- **SelectorOption-rendered content**: Option content rendered with SelectorOption, either by the default renderer or by renderOption when it returns SelectorOption.
- **Bare caller-rendered option content**: Arbitrary content returned directly by renderOption without opting into SelectorOption.
- **Option selection indicator**: Resolved selection mark rendered for each option in its checked or unchecked state. Its layout space collapses when the resolved indicator draws nothing.
- **Option divider**: Divider supplied in the public options data to separate adjacent option groups.
- **Section heading**: Visible heading for a labeled group of option rows.
- **Empty state**: Message shown when the shared panel content has no options or no search matches.
- **Pointer popup**: Anchored painted surface that hosts the shared panel content for popover presentation.
- **Touch sheet heading**: Heading above the shared panel content in bottom-sheet presentation.
- **Touch sheet**: BottomSheet surface that hosts the same panel content for bottom-sheet presentation.

## Selector Option

用于在 Selector 的 renderOption 渲染函数中自定义选项渲染的辅助组件。

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label * | ReactNode |  | 选项的主标签文本。 |
| icon | IconType |  | 显示在标签前的图标。 |
| description | ReactNode |  | 显示在标签下方的次要描述文本。 |
| layout | 'stacked' \| 'inline' | 'stacked' | How the label and description sit together. 'stacked' puts the description on its own line; 'inline' keeps both on one line so the row fits a fixed-height host. Inside a Selector trigger the trigger's padding sizes itself to whichever layout you pick, so both land on the 4px rhythm; an InputGroup pins the row height and forces 'inline'. |
| endContent | ReactNode |  | 在标签和描述之后渲染的附加内容。 |


## 完整示例的额外依赖

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

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

---

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