> Leaf UI 0.6.0 · Package: @ifhover/leaf-ui · Source: b30c3d3a8fa9c5e15d0d0522cebfbd33f60c6b06
> Browser documentation: https://ifhover.github.io/leaf-ui/v/0.6.0/components/select.html

> For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/v/0.6.0/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/v/0.6.0/llms-full.txt.

# Select 选择器

用于从下拉列表中选择一项或多项。

## 基础选择

通过 options 传入选项；placeholder 在空值时提示选择，单个选项可以禁用。


选择团队

默认团队

禁用团队

basic.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/basic.tsx"
import { Form, FormField, Select } from '@ifhover/leaf-ui';

const options = [
  { label: '设计工作室', value: 'design' },
  { label: '产品团队', value: 'product' },
  { label: '工程团队', value: 'engineering' },
  { label: '归档项目', value: 'archived', disabled: true },
];

export function SelectBasic() {
  return (
    <Form layout="vertical">
      <FormField label="选择团队">
        <Select aria-label="选择团队" options={options} placeholder="请选择团队" />
      </FormField>
      <FormField label="默认团队">
        <Select aria-label="默认团队" options={options} defaultValue="design" />
      </FormField>
      <FormField label="禁用团队">
        <Select aria-label="禁用团队" options={options} disabled defaultValue="product" />
      </FormField>
    </Form>
  );
}
```

展开代码

## 受控与校验

value / onChange 管理选择结果，回调直接返回字符串值和选项对象。`allowClear` 显示清除按钮；status="error" 同时提供错误外观与 aria-invalid。


项目可见性

当前值：尚未选择

未设置团队 · 错误

controlled.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/controlled.tsx"
import { Form, FormField, Select, Text } from '@ifhover/leaf-ui';

import { useState } from 'react';

export function SelectControlled() {
  const [value, setValue] = useState('');
  return (
    <Form layout="vertical">
      <FormField label="项目可见性">
        <Select
          aria-label="项目可见性"
          value={value}
          placeholder="选择可见性"
          allowClear
          onChange={setValue}
          options={[
            { label: '公开', value: 'public' },
            { label: '团队', value: 'team' },
            { label: '私密', value: 'private' },
          ]}
        />
      </FormField>
      <Text tone="muted">当前值：{value || '尚未选择'}</Text>
      <FormField label="未设置团队 · 错误">
        <Select
          aria-label="未设置团队"
          status="error"
          placeholder="请选择所属团队"
          options={[{ label: '设计团队', value: 'design' }]}
        />
      </FormField>
    </Form>
  );
}
```

展开代码

## 尺寸对齐


sm查询

md查询

lg查询

control-sizes.tsx

复制代码

```tsx lineNumbers file="../../src/examples/guide/control-sizes.tsx"
import type { ControlSize } from '@ifhover/leaf-ui';

import { Button, Flex, Input, Select, Text } from '@ifhover/leaf-ui';

export function ControlSizes() {
  return (
    <Flex direction="column" gap="md">
      {(['sm', 'md', 'lg'] as const).map((size: ControlSize) => (
        <Flex key={size} align="center" gap="sm" wrap>
          <Text tone="muted">{size}</Text>
          <Input size={size} aria-label={`${size} 搜索`} placeholder="搜索项目" />
          <Select
            size={size}
            aria-label={`${size} 状态`}
            defaultValue="all"
            options={[
              { label: '全部状态', value: 'all' },
              { label: '进行中', value: 'active' },
            ]}
          />
          <Button size={size}>查询</Button>
        </Flex>
      ))}
    </Flex>
  );
}
```

展开代码

## 搜索与多选

showSearch 只筛选已有选项；multiple 使用数组值并在选择后保持展开，可移除标签。


搜索团队

多选团队

设计

design

search.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/search.tsx"
import type { SelectOption } from '@ifhover/leaf-ui';

import { Form, FormField, Select, Text } from '@ifhover/leaf-ui';

import { useState } from 'react';

const options: SelectOption[] = [
  { value: 'design', label: '设计' },
  { value: 'engineering', label: '研发' },
  { value: 'marketing', label: '市场' },
];

export function SelectSearch() {
  const [teams, setTeams] = useState<string[]>(['design']);
  return (
    <Form layout="vertical">
      <FormField label="搜索团队">
        <Select
          aria-label="搜索团队"
          showSearch
          allowClear
          options={options}
          placeholder="输入搜索团队"
        />
      </FormField>
      <FormField label="多选团队">
        <Select
          aria-label="多选团队"
          multiple
          showSearch
          allowClear
          options={options}
          value={teams}
          onChange={setTeams}
        />
      </FormField>
      <output>
        <Text tone="muted">{teams.join(', ') || '—'}</Text>
      </output>
    </Form>
  );
}
```

展开代码

## 浮层宽度与内容


自动浮层宽度

固定浮层宽度

多选数量限制

Leaf Studio+1Leaf Notes

加载选项

空选项

popup.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/popup.tsx"
import type { SelectOption } from '@ifhover/leaf-ui';

import { Form, FormField, Select } from '@ifhover/leaf-ui';

const options: SelectOption[] = [
  { value: 'garden', label: 'Leaf Garden Design System' },
  { value: 'studio', label: 'Leaf Studio' },
  { value: 'notes', label: 'Leaf Notes' },
];

export function SelectPopup() {
  return (
    <Form layout="vertical">
      <FormField label="自动浮层宽度">
        <Select
          aria-label="自动浮层宽度"
          style={{ width: 180 }}
          options={options}
          defaultValue="studio"
        />
      </FormField>
      <FormField label="固定浮层宽度">
        <Select
          aria-label="固定浮层宽度"
          popupWidth={300}
          popupMaxWidth={360}
          style={{ width: 180 }}
          options={options}
          optionRender={(option) => (
            <div>
              <strong>{option.label}</strong>
              <div style={{ fontSize: 12, opacity: 0.65 }}>{option.value}</div>
            </div>
          )}
        />
      </FormField>
      <FormField label="多选数量限制">
        <Select
          aria-label="多选数量限制"
          multiple
          options={options}
          defaultValue={['studio', 'notes']}
          maxCount={2}
          maxTagCount={1}
          allowClear
        />
      </FormField>
      <FormField label="加载选项">
        <Select aria-label="加载选项" options={[]} loading placeholder="加载中" />
      </FormField>
      <FormField label="空选项">
        <Select aria-label="空选项" options={[]} notFoundContent="还没有项目" />
      </FormField>
    </Form>
  );
}
```

展开代码

## 扩展用法


分组与自由创建5000 个选项

extensions.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/extensions.tsx"
import { Select, Space } from '@ifhover/leaf-ui';

const many = Array.from({ length: 5000 }, (_, i) => ({
  value: String(i),
  label: `项目 ${i + 1}`,
}));

export function ExtensionDemo() {
  return (
    <Space direction="vertical" align="stretch">
      <label htmlFor="select-extension-1">
        分组与自由创建
        <Select
          id="select-extension-1"
          multiple
          showSearch
          allowCreate
          options={[
            {
              label: '设计',
              options: [
                { value: 'ui', label: '界面设计' },
                { value: 'ux', label: '体验设计' },
              ],
            },
            { label: '工程', options: [{ value: 'web', label: '前端开发' }] },
          ]}
          placeholder="选择或创建标签"
        />
      </label>
      <label htmlFor="select-extension-2">
        5000 个选项
        <Select
          id="select-extension-2"
          showSearch
          virtual
          options={many}
          placeholder="搜索并选择"
        />
      </label>
    </Space>
  );
}
```

展开代码

## 选中内容与受控浮层


根据宽度自动折叠标签

DesignEngineeringMarketing+3

受控浮层与附加操作

capabilities.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/capabilities.tsx"
import { Button, Flex, Select, Space, Tag, Text } from '@ifhover/leaf-ui';

import { useState } from 'react';

const options = ['Design', 'Engineering', 'Marketing', 'Operations'].map((label) => ({
  value: label,
  label,
}));

export function Capabilities() {
  const [open, setOpen] = useState(false);
  return (
    <Space direction="vertical" align="stretch">
      <Flex direction="column" gap="sm" align="center">
        <Text>
          {'根据宽度自动折叠标签'}
        </Text>
        <Select
          multiple
          showSearch
          defaultValue={['Design', 'Engineering', 'Marketing']}
          options={options}
          maxTagCount="responsive"
          tagRender={(option, remove) => (
            <Tag closable onClose={remove}>
              {option.label}
            </Tag>
          )}
        />
      </Flex>
      <Flex direction="column" gap="sm" align="center">
        <Text>{'受控浮层与附加操作'}</Text>
        <Select
          options={options}
          open={open}
          onOpenChange={setOpen}
          labelRender={(option) => <strong>{option.label}</strong>}
          popupRender={(content) => (
            <>
              {content}
              <Button size="sm" variant="ghost" onClick={() => setOpen(false)}>
                {'完成'}
              </Button>
            </>
          )}
        />
      </Flex>
    </Space>
  );
}
```

展开代码

## 单行多选

设置 `tagWrap={false}` 后，已选项始终展示在一行内。长文字自动省略，放不下的选项合并为 `+N`；空间很窄时只显示数量，打开浮层可查看和修改选择。`maxTagCount` 可进一步限制标签数量，`maxTagCount="responsive"` 也会启用单行自动折叠。


单行显示 · 长标签自动省略

一个名称比较长的产品设计团队前端开发服务端开发日常运营+4

默认换行

一个名称比较长的产品设计团队前端开发服务端开发日常运营

single-line.tsx

复制代码

```tsx lineNumbers file="../../src/examples/select/single-line.tsx"
import { Form, FormField, Select } from '@ifhover/leaf-ui';

const options = [
  { value: '0', label: '一个名称比较长的产品设计团队' },
  { value: '1', label: '前端开发' },
  { value: '2', label: '服务端开发' },
  { value: '3', label: '日常运营' },
];

const selected = options.map((option) => option.value);

export function SingleLine() {
  return (
    <Form layout="vertical">
      <FormField label="单行显示 · 长标签自动省略">
        <Select
          multiple
          showSearch
          allowClear
          tagWrap={false}
          options={options}
          defaultValue={selected}
        />
      </FormField>
      <FormField label="默认换行">
        <Select multiple options={options} defaultValue={selected} />
      </FormField>
    </Form>
  );
}
```

展开代码

## Select API

| 属性                  | 类型                                                                                                                                                                   | 默认值          | 说明                                           |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -------------------------------------------- |
| `popupPlacement`    | `'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end' \| 'right' \| 'right-start' \| 'right-end'` | —            | 浮层相对触发控件的位置。                                 |
| `popupClassName`    | `string`                                                                                                                                                             | —            | 浮层样式                                         |
| `popupStyle`        | `CSSProperties`                                                                                                                                                      | —            | 浮层样式                                         |
| `popupRender`       | `(content: ReactNode) => ReactNode`                                                                                                                                  | —            | 包装或补充面板内容，保留传入的内容以维持交互。                      |
| `getPopupContainer` | `() => Element \| DocumentFragment`                                                                                                                                  | —            | 返回浮层的挂载容器，也可返回 ShadowRoot。                   |
| `options`           | `readonly (`[`SelectOption`](#selectoption)`\|`[`SelectOptionGroup`](#selectoptiongroup-api)`)[]`                                                                    | 必填           | 必需。选项列表                                      |
| `size`              | `'sm' \| 'md' \| 'lg'`                                                                                                                                               | `'md'`       | 选择器尺寸                                        |
| `status`            | `'error' \| 'warning'`                                                                                                                                               | —            | 校验状态                                         |
| `allowClear`        | `boolean`                                                                                                                                                            | `false`      | 展示清除按钮                                       |
| `showSearch`        | `boolean`                                                                                                                                                            | `false`      | 允许输入搜索选项                                     |
| `filterOption`      | `boolean \| ((query: string, option: `[`SelectOption`](#selectoption)`) => boolean)`                                                                                 | `true`       | 自定义筛选，false 用于外部筛选                           |
| `onSearch`          | `(query: string) => void`                                                                                                                                            | —            | 搜索文字变化                                       |
| `onOpenChange`      | `(open: boolean) => void`                                                                                                                                            | —            | 展开或收起选项面板                                    |
| `open`              | `boolean`                                                                                                                                                            | —            | 控制面板是否展开。                                    |
| `defaultOpen`       | `boolean`                                                                                                                                                            | —            | 面板初始展开状态。                                    |
| `inputReadOnly`     | `boolean`                                                                                                                                                            | —            | 只禁止手动输入，仍可在面板中选择。                            |
| `labelRender`       | `(option: `[`SelectOption`](#selectoption)`) => ReactNode`                                                                                                           | —            | 自定义选中值的展示。                                   |
| `tagRender`         | `(option: `[`SelectOption`](#selectoption)`, remove: () => void) => ReactNode`                                                                                       | —            | 自定义选中标签；通过 remove 请求移除。                      |
| `onPopupScroll`     | `(event: React.UIEvent<HTMLDivElement>) => void`                                                                                                                     | —            | 浮层滚动事件，可用来加载下一页。                             |
| `popupWidth`        | `number \| string`                                                                                                                                                   | `'auto'`     | 浮层宽度；auto 按内容扩宽，trigger 与控件等宽，也可指定像素或 CSS 长度 |
| `popupMaxWidth`     | `number \| string`                                                                                                                                                   | `420`        | 最大宽度，仍受视口限制                                  |
| `loading`           | `boolean`                                                                                                                                                            | `false`      | 加载提示                                         |
| `notFoundContent`   | `ReactNode`                                                                                                                                                          | —            | 没有选项时的内容                                     |
| `optionRender`      | `(option: `[`SelectOption`](#selectoption)`) => ReactNode`                                                                                                           | —            | 自定义选项内容                                      |
| `maxCount`          | `number`                                                                                                                                                             | —            | 多选数量上限，已选项可继续取消                              |
| `maxTagCount`       | `number \| 'responsive'`                                                                                                                                             | —            | 最多展示的标签数；responsive 根据可用宽度自动折叠。              |
| `tagWrap`           | `boolean`                                                                                                                                                            | `true`       | 允许多选标签换行；false 单行显示并自动折叠溢出项。                 |
| `allowCreate`       | `boolean`                                                                                                                                                            | `false`      | 允许创建未匹配的选项                                   |
| `onCreate`          | `(option: `[`SelectOption`](#selectoption)`) => void`                                                                                                                | —            | 新选项创建回调                                      |
| `virtual`           | `boolean`                                                                                                                                                            | 选项超过 100 时启用 | 仅渲染可见项目                                      |
| `listHeight`        | `number`                                                                                                                                                             | `248`        | 列表高度                                         |
| `optionHeight`      | `number`                                                                                                                                                             | `32`         | 跟随区域控件高度和密度的预估行高；实际行会被测量。                    |
| `multiple`          | `false \| true`                                                                                                                                                      | `false`      | 多选时值为数组，默认 \[]                               |
| `value`             | `string \| readonly string[]`                                                                                                                                        | `''`         | 受控值 / 非受控初始值                                 |
| `defaultValue`      | `string \| readonly string[]`                                                                                                                                        | `''`         | 受控值 / 非受控初始值                                 |
| `onChange`          | `((value: string, option?: `[`SelectOption`](#selectoption)`) => void) \| ((value: string[], options: `[`SelectOption`](#selectoption)`[]) => void)`                 | —            | 返回值与选项；清除时返回空字符串和 `undefined`                |
| `disabled`          | `boolean`                                                                                                                                                            | `false`      | 禁用交互。                                        |
| `name`              | `string`                                                                                                                                                             | —            | 原生 FormData 中的字段名。                           |
| `form`              | `string`                                                                                                                                                             | —            | 关联的原生表单 ID。                                  |
| `required`          | `boolean`                                                                                                                                                            | `false`      | 要求整体值非空，复合控件只校验一次。                           |
| `ref`               | `Ref<HTMLInputElement>`                                                                                                                                              | —            | 输入框引用                                        |

支持 name、required、form 和非受控表单 reset；其余原生 input 属性及 aria-\* 透传至输入框。className 与 style 作用于外层容器。选项 value 应唯一，不要将空字符串用作业务选项值。

## 类型

[`SelectProps`](#select-api) 用于定义组件属性。单独组装选项列表时，可以使用 [`SelectOption`](#selectoption)：

```ts
import type { SelectProps, SelectOption, ControlSize, ControlStatus } from '@ifhover/leaf-ui';

const options: SelectOption[] = [
  { label: '设计团队', value: 'design' },
  { label: '研发团队', value: 'engineering' },
];
```

### SelectOption

| 属性            | 类型          | 默认值     | 说明                             |
| ------------- | ----------- | ------- | ------------------------------ |
| `value`       | `string`    | 必填      | 唯一选项值，不能使用空字符串                 |
| `searchLabel` | `string`    | —       | 搜索文字；默认使用文字 label，复杂节点使用 value |
| `label`       | `ReactNode` | 必填      | 选项内容，可以是文字或 React 节点           |
| `disabled`    | `boolean`   | `false` | 禁用该选项                          |

## 使用说明

为选择器提供 label 或 aria-label。FormData 提交选项的字符串 value，清除后为空字符串；受控组件的表单重置需同步重置自己的状态。

Enter、空格或方向键打开面板，↑ / ↓ 移动高亮并跳过禁用选项，Home / End 跳至首尾，Enter 或空格确认。Escape 收起，Tab 继续访问后续控件。点击外部或移走焦点会收起。

浮层随页面滚动定位，并在视口边缘避让；长列表可滚动。支持单选、多选及搜索，适用于选项数量适中的场景。

多选清除返回 `[]`；FormData 在同一个 name 下提交多个值，通过 `formData.getAll(name)` 读取。搜索文字为空时，Backspace 移除最后一个可用标签。离开控件会清空搜索文字，默认不创建选项；设置 allowCreate 可创建未匹配的选项。label 为 React 节点时，用 searchLabel 指定搜索文字。ref 与原生 input 属性作用于输入框。

控件保持页面中的布局宽度，浮层默认至少与控件等宽，并按选项内容扩宽，最多达到 popupMaxWidth。使用 `popupWidth="trigger"` 保持等宽，或通过 `popupWidth={300}` 指定宽度。内容超过最大宽度时才换行。

## 扩展类型

```ts
import type { SelectOptionGroup } from '@ifhover/leaf-ui';
```

## SelectOptionGroup API

| 属性        | 类型                                             | 默认值 | 说明      |
| --------- | ---------------------------------------------- | --- | ------- |
| `label`   | `ReactNode`                                    | 必填  | 必需。显示内容 |
| `options` | `readonly `[`SelectOption`](#selectoption)`[]` | 必填  | 必需。可用选项 |

## SelectOption

| 属性            | 类型          | 默认值 | 说明          |
| ------------- | ----------- | --- | ----------- |
| `value`       | `string`    | —   | 必需。受控值。     |
| `label`       | `ReactNode` | —   | 必需。展示文字或内容。 |
| `disabled`    | `boolean`   | —   | 禁用交互。       |
| `searchLabel` | `string`    | —   | 搜索时使用的纯文本。  |
