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, and this page is available as Markdown at https://ifhover.github.io/leaf-ui/v/0.6.0/components/select.md.
  • 简体中文
  • Select 选择器

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

    基础选择

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

    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
    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
    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
    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
    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>
      );
    }
    

    扩展用法

    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
    受控浮层与附加操作
    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" 也会启用单行自动折叠。

    一个名称比较长的产品设计团队前端开发服务端开发日常运营
    一个名称比较长的产品设计团队前端开发服务端开发日常运营
    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'—浮层相对触发控件的位置。
    popupClassNamestring—浮层样式
    popupStyleCSSProperties—浮层样式
    popupRender(content: ReactNode) => ReactNode—包装或补充面板内容,保留传入的内容以维持交互。
    getPopupContainer() => Element | DocumentFragment—返回浮层的挂载容器,也可返回 ShadowRoot。
    optionsreadonly (SelectOption|SelectOptionGroup)[]必填必需。选项列表
    size'sm' | 'md' | 'lg''md'选择器尺寸
    status'error' | 'warning'—校验状态
    allowClearbooleanfalse展示清除按钮
    showSearchbooleanfalse允许输入搜索选项
    filterOptionboolean | ((query: string, option: SelectOption) => boolean)true自定义筛选,false 用于外部筛选
    onSearch(query: string) => void—搜索文字变化
    onOpenChange(open: boolean) => void—展开或收起选项面板
    openboolean—控制面板是否展开。
    defaultOpenboolean—面板初始展开状态。
    inputReadOnlyboolean—只禁止手动输入,仍可在面板中选择。
    labelRender(option: SelectOption) => ReactNode—自定义选中值的展示。
    tagRender(option: SelectOption, remove: () => void) => ReactNode—自定义选中标签;通过 remove 请求移除。
    onPopupScroll(event: React.UIEvent<HTMLDivElement>) => void—浮层滚动事件,可用来加载下一页。
    popupWidthnumber | string'auto'浮层宽度;auto 按内容扩宽,trigger 与控件等宽,也可指定像素或 CSS 长度
    popupMaxWidthnumber | string420最大宽度,仍受视口限制
    loadingbooleanfalse加载提示
    notFoundContentReactNode—没有选项时的内容
    optionRender(option: SelectOption) => ReactNode—自定义选项内容
    maxCountnumber—多选数量上限,已选项可继续取消
    maxTagCountnumber | 'responsive'—最多展示的标签数;responsive 根据可用宽度自动折叠。
    tagWrapbooleantrue允许多选标签换行;false 单行显示并自动折叠溢出项。
    allowCreatebooleanfalse允许创建未匹配的选项
    onCreate(option: SelectOption) => void—新选项创建回调
    virtualboolean选项超过 100 时启用仅渲染可见项目
    listHeightnumber248列表高度
    optionHeightnumber32跟随区域控件高度和密度的预估行高;实际行会被测量。
    multiplefalse | truefalse多选时值为数组,默认 []
    valuestring | readonly string[]''受控值 / 非受控初始值
    defaultValuestring | readonly string[]''受控值 / 非受控初始值
    onChange((value: string, option?: SelectOption) => void) | ((value: string[], options: SelectOption[]) => void)—返回值与选项;清除时返回空字符串和 undefined
    disabledbooleanfalse禁用交互。
    namestring—原生 FormData 中的字段名。
    formstring—关联的原生表单 ID。
    requiredbooleanfalse要求整体值非空,复合控件只校验一次。
    refRef<HTMLInputElement>—输入框引用

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

    类型

    SelectProps 用于定义组件属性。单独组装选项列表时,可以使用 SelectOption:

    import type { SelectProps, SelectOption, ControlSize, ControlStatus } from '@ifhover/leaf-ui';
    
    const options: SelectOption[] = [
      { label: '设计团队', value: 'design' },
      { label: '研发团队', value: 'engineering' },
    ];

    SelectOption

    属性类型默认值说明
    valuestring必填唯一选项值,不能使用空字符串
    searchLabelstring—搜索文字;默认使用文字 label,复杂节点使用 value
    labelReactNode必填选项内容,可以是文字或 React 节点
    disabledbooleanfalse禁用该选项

    使用说明

    为选择器提供 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} 指定宽度。内容超过最大宽度时才换行。

    扩展类型

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

    SelectOptionGroup API

    属性类型默认值说明
    labelReactNode必填必需。显示内容
    optionsreadonly SelectOption[]必填必需。可用选项

    SelectOption

    属性类型默认值说明
    valuestring—必需。受控值。
    labelReactNode—必需。展示文字或内容。
    disabledboolean—禁用交互。
    searchLabelstring—搜索时使用的纯文本。