跳到主要内容

GenericSelect

弹出式选择控件的抽象基座:一个具备 Ant Design Select 外观的触发器,其下拉内容被替换为任意自定义界面。它负责样式化触发器(选中值展示、size/status/variant/清除/禁用)、受控的展开状态、触发器内的搜索框以及弹出层容器;具体组件只需提供弹出层内容以及值的渲染方式。

VEF 专属组件。 并非 Ant Design 的一部分。它是 IconPicker 所基于的基座。

何时使用

  • 需要构建一个自定义的下拉式选择控件——例如图标网格、数据表选择器、色板选择器——它需要 Select 的触发器外观(值展示、size/status/variant、清除按钮、搜索框),但弹出层不是一个简单列表。
  • 当扁平的选项列表就足够时,直接使用 Select;当弹出层需要自定义组件时,使用 GenericSelect

基础用法

import { GenericSelect } from '@vef-framework-react/components';
import type { GenericSelectPopupApi } from '@vef-framework-react/components';
import { useState } from 'react';

function ColorPopup({ value, select, close }: GenericSelectPopupApi<string>) {
return (
<div style={{ display: 'flex', gap: 8, padding: 8 }}>
{['red', 'green', 'blue'].map((color) => (
<button
key={color}
style={{ background: color, width: 24, height: 24 }}
onClick={() => {
select(color);
close();
}}
/>
))}
</div>
);
}

export default function Demo() {
const [color, setColor] = useState<string | null>(null);

return (
<GenericSelect<string>
placeholder="Pick a color"
value={color}
renderLabel={(value) => value}
renderPopup={(api) => <ColorPopup {...api} />}
onChange={setColor}
/>
);
}

弹出层 API

renderPopup 会接收一个 GenericSelectPopupApi 对象,因此弹出层内容永远不需要触碰底层的 antd Select

成员类型说明
valueTValue | null当前选中值,用于高亮激活的选项
keywordstring在触发器中输入的、经过防抖处理的搜索关键字;非搜索状态下为 ""
openboolean弹出层当前是否展开——可用于将开销较大的工作(数据获取、虚拟化测量)推迟到首次打开时进行
select(value: TValue) => void提交一个值,转发给 onChange不会关闭弹出层
close() => void关闭弹出层,不改变已选中的值

自定义标签渲染

<GenericSelect<string>
value={value}
renderLabel={(value) => <strong>{value}</strong>}
renderPopup={renderPopup}
onChange={setValue}
/>

受控展开状态

const [isOpen, setIsOpen] = useState(false);

<GenericSelect<string>
open={isOpen}
renderPopup={renderPopup}
onOpenChange={setIsOpen}
/>

API

GenericSelectProps<TValue>

TValue 是可选择的值类型,会被转发给底层的 antd Select 作为其值——必须是 stringnumber

Prop类型默认值说明
valueTValue | null已选中的值;null/undefined 时渲染占位内容
onChange(value: TValue | null) => void从弹出层提交值时触发,清除时为 null
onBlur() => void触发器失焦时触发
openboolean弹出层的受控展开状态
defaultOpenbooleanfalse非受控模式下的初始展开状态;提供 open 时忽略
onOpenChange(open: boolean) => void弹出层展开或关闭时触发
renderPopup(api: GenericSelectPopupApi<TValue>) => ReactNode必填渲染弹出层内容
renderLabel(value: TValue) => ReactNode原始值渲染触发器内选中的值
searchablebooleantrue在触发器中显示搜索框,并将关键字转发给弹出层
placeholderReactNode未选中值时显示的占位内容
size'small' | 'medium' | 'large''medium'密度预设
status'error' | 'warning'校验状态
variant'outlined' | 'filled' | 'borderless' | 'underlined''outlined'触发器的视觉变体
disabledbooleanfalse禁用控件
allowClearbooleanfalse显示清除按钮,将值重置为 null
loadingbooleanfalse将触发器渲染为加载状态
suffixIconReactNode自定义后缀图标;当弹出层展开且 searchable 时会被搜索图标覆盖
prefixReactNode渲染在触发器内值之前的内容
getPopupContainer(triggerNode: HTMLElement) => HTMLElementdocument.body弹出层渲染到的容器元素
popupMatchSelectWidthboolean | numberfalse弹出层宽度是否与触发器宽度一致
popupClassNamestring弹出层根节点的附加类名
classNamestring触发器上的附加类名
styleCSSProperties触发器上的内联样式

GenericSelectRef

成员类型说明
focus() => void将焦点移入触发器
blur() => void移除触发器焦点

GenericSelectStatus / GenericSelectVariant

type GenericSelectStatus = "error" | "warning";
type GenericSelectVariant = "outlined" | "filled" | "borderless" | "underlined";