选择器
查看源代码当用户需要从一组同类数据中选择一个或多个时,可以使用下拉选择器,点击后选择对应项。
演示
import { Select, SelectContent, SelectList, SelectEmpty } from "@resolid/react-ui";
export default function App() {
const collection = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes", disabled: true },
{ value: "pineapple", label: "Pineapple" },
];
return (
<Select className={"w-50"} collection={collection}>
<SelectContent className={"p-1"}>
<SelectEmpty />
<SelectList />
</SelectContent>
</Select>
);
}
用法
import {
Select,
SelectFilter,
SelectContent,
SelectEmpty,
SelectVirtualizer,
SelectList,
} from "@resolid/react-ui";
Select
: 选择器的根容器。SelectFilter
: 选择器筛选。SelectContent
: 选择器要渲染的内容。SelectEmpty
: 选择器搜索结果为空时显示。SelectVirtualizer
: 选择器列表虚拟化。SelectList
: 选择器的项目列表。
<Select>
<SelectFilter />
<SelectContent>
<SelectList />
</SelectContent>
</Select>
特点
- 使用 WAI ARIA Listbox 设计模式作为 Select 辅助技术。
- 可以控制或不受控制。
- 全键盘导航。
- 支持单选和多选。
- 支持禁用选项。
- 支持项目、标签、项目组。
- 支持自定义占位符。
举例
可控值
使用 value
和 onChange
属性来控制选择的值。
import { Select, SelectContent, SelectList } from "@resolid/react-ui";
import { useState } from "react";
export default function App() {
const [value, setValue] = useState([]);
const collection = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes", disabled: true },
{ value: "pineapple", label: "Pineapple" },
];
return (
<div className={"flex flex-col gap-2"}>
<p>你选择了水果: {value.join(", ")}</p>
<Select className={"w-50"} value={value} onChange={setValue} collection={collection}>
<SelectContent className={"p-1"}>
<SelectList></SelectList>
</SelectContent>
</Select>
</div>
);
}
默认值
import { Select, SelectContent, SelectList } from "@resolid/react-ui";
export default function App() {
const collection = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes", disabled: true },
{ value: "pineapple", label: "Pineapple" },
];
return (
<div className={"flex gap-3"}>
<Select defaultValue={"blueberry"} className={"w-50"} collection={collection}>
<SelectContent className={"p-1"}>
<SelectList></SelectList>
</SelectContent>
</Select>
<Select
multiple
defaultValue={["apple", "pineapple"]}
className={"min-w-50"}
collection={collection}
>
<SelectContent className={"p-1"}>
<SelectList></SelectList>
</SelectContent>
</Select>
</div>
);
}
多选
import { Select, SelectContent, SelectList } from "@resolid/react-ui";
export default function App() {
const collection = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes", disabled: true },
{ value: "pineapple", label: "Pineapple" },
];
return (
<Select className={"min-w-50"} multiple collection={collection}>
<SelectContent className={"p-1"}>
<SelectList></SelectList>
</SelectContent>
</Select>
);
}
可筛选
import { Select, SelectFilter, SelectContent, SelectEmpty, SelectList } from "@resolid/react-ui";
export default function App() {
const collection = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes", disabled: true },
{ value: "pineapple", label: "Pineapple" },
];
return (
<Select className={"min-w-50"} collection={collection}>
<div className={"p-1 pb-0"}>
<SelectFilter size={"sm"} />
</div>
<SelectContent className={"p-1"}>
<SelectEmpty />
<SelectList />
</SelectContent>
</Select>
);
}
虚拟化
使用 SelectVirtualizer 组件可以实现虚拟化
import {
Select,
SelectFilter,
SelectContent,
SelectList,
SelectVirtualizer,
} from "@resolid/react-ui";
export default function App() {
const collection = Array.from({ length: 1000 }, (_, i) => ({
value: i + 1,
label: `Item ${i + 1}`,
}));
const groupCollection = Array.from({ length: Math.ceil(collection.length / 20) }, (_, i) => ({
label: `Group ${i + 1}`,
children: collection.slice(i * 20, (i + 1) * 20),
}));
return (
<div className={"flex gap-3"}>
<Select className={"w-50"} collection={collection}>
<SelectContent className={"max-h-60 px-1"}>
<SelectVirtualizer>
<SelectList />
</SelectVirtualizer>
</SelectContent>
</Select>
<Select className={"w-50"} collection={groupCollection}>
<SelectContent className={"max-h-60 px-1"}>
<SelectVirtualizer>
<SelectList />
</SelectVirtualizer>
</SelectContent>
</Select>
</div>
);
}
自定义渲染
import { Select, SelectFilter, SelectContent, SelectList } from "@resolid/react-ui";
import { BrowserIcon } from "~/components/browser-icon";
export default function App() {
const collection = [
{
value: "chrome",
label: "Chrome",
description: "Google Chrome 是一款快速、易于使用且安全的网络浏览器。",
},
{
value: "firefox",
label: "Firefox",
description: "Firefox 是一款快速、轻量、注重隐私的浏览器,全平台可用。",
},
{
value: "microsoft-edge",
label: "Microsoft Edge",
description: "Microsoft Edge 是 AI 驱动的浏览器。更智能的浏览方式。",
},
{
value: "safari",
label: "Safari",
description: "Safari 是在所有 Apple 设备上体验互联网的最佳方式。",
},
{
value: "opera",
label: "Opera",
description: "比默认浏览器更快、更安全、更智能。 功能齐全,可保护隐私、安全等。",
},
];
return (
<Select
className={"w-50"}
collection={collection}
renderValue={(item) => {
return (
<div className={"flex items-center gap-1"}>
<BrowserIcon size={"1em"} name={item.value} />
<span>{item.label}</span>
</div>
);
}}
renderItem={(item, { selected }) => {
return (
<div className={"flex items-center gap-2"}>
<div className={"w-12 flex-1"}>
<BrowserIcon size={"2em"} name={item.value} />
</div>
<div className={"flex flex-col gap-1"}>
<div>{item.label}</div>
<div className={`text-sm ${!selected ? "text-fg-subtle" : ""}`}>
{item.description}
</div>
</div>
</div>
);
}}
>
<SelectContent className={"max-w-90 p-1"}>
<SelectList />
</SelectContent>
</Select>
);
}
原生选择器
import { NativeSelect } from "@resolid/react-ui";
export default function App() {
const collection = [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "blueberry", label: "Blueberry" },
{ value: "grapes", label: "Grapes", disabled: true },
{ value: "pineapple", label: "Pineapple" },
];
return (
<div className={"flex items-center gap-2"}>
{["xs", "sm", "md", "lg", "xl"].map((size) => (
<NativeSelect key={size} placeholder={"选择器"} size={size}>
{collection.map((item) => (
<option key={item.value} value={item.value} disabled={item.disabled}>
{item.label}
</option>
))}
</NativeSelect>
))}
</div>
);
}
属性
Select
属性 | 类型 | 默认值 | 必须 | |
---|---|---|---|---|
属性 duration | 动画持续时间 | 类型number | 默认值250 | 必须false |
属性 multiple | 是否多选 | 类型boolean | 默认值false | 必须false |
属性 collection | 项目的集合 | 类型T[] | 默认值- | 必须true |
属性 valueKey | 自定义 `value` 字段名 | 类型string | 默认值"value" | 必须false |
属性 labelKey | 自定义 `label` 字段名 | 类型string | 默认值"label" | 必须false |
属性 disabledKey | 自定义 `disabled` 字段名 | 类型string | 默认值"disabled" | 必须false |
属性 childrenKey | 自定义 `children` 字段名 | 类型string | 默认值"children" | 必须false |
属性 searchFilter | 自定义过滤函数 | 类型(keyword: string, item: T) => boolean | 默认值- | 必须false |
属性 renderItem | 自定义项目渲染 | 类型(item: T, status: { active: boolean; selected: boolean; }) => ReactNode | 默认值- | 必须false |
属性 renderGroupLabel | 自定义组标签渲染 | 类型(group: T) => ReactNode | 默认值- | 必须false |
属性 size | 大小 | 类型"xs" | "sm" | "md" | "lg" | "xl" | 默认值"md" | 必须false |
属性 renderValue | 自定义值渲染 | 类型(item: T) => ReactNode | 默认值- | 必须false |
属性 placeholder | 占位符 | 类型string | 默认值- | 必须false |
属性 closeOnSelect | 选择后关闭 | 类型boolean | 默认值true | 必须false |
属性 value | 受控值 | 类型string | number | (string | number)[] | null | 默认值- | 必须false |
属性 defaultValue | 默认值 | 类型string | number | (string | number)[] | null | 默认值null | [] | 必须false |
属性 onChange | onChange 回调 | 类型((value: string | number | (string | number)[] | null) => void) | 默认值- | 必须false |
属性 name | 字段的名称, 提交表单时使用 | 类型string | 默认值- | 必须false |
属性 open | 受控打开状态 | 类型boolean | 默认值- | 必须false |
属性 defaultOpen | 初始渲染时的默认打开状态 | 类型boolean | 默认值false | 必须false |
属性 onOpenChange | 打开状态改变时调用 | 类型(open: boolean) => void | 默认值- | 必须false |
属性 disabled | 是否禁用 | 类型boolean | 默认值false | 必须false |
属性 required | 是否必需 | 类型boolean | 默认值false | 必须false |
属性 readOnly | 是否只读 | 类型boolean | 默认值false | 必须false |
属性 invalid | 是否无效 | 类型boolean | 默认值false | 必须false |
SelectVirtualizer
属性 | 类型 | 默认值 | 必须 | |
---|---|---|---|---|
属性 itemHeight | 项目的高度(以像素为单位) | 类型number | 默认值- | 必须false |
属性 groupLabelHeight | 分组标签的高度(以像素为单位) | 类型number | 默认值- | 必须false |
属性 overscan | 在可见区域上方和下方渲染的项目数 | 类型number | 默认值3 | 必须false |
属性 paddingStart | 应用于虚拟器开始处的填充(以像素为单位) | 类型number | 默认值4 | 必须false |
属性 paddingEnd | 应用于虚拟器末尾的填充(以像素为单位) | 类型number | 默认值4 | 必须false |
属性 scrollPaddingStart | 滚动到元素时应用于虚拟器开始处的填充(以像素为单位) | 类型number | 默认值17 | 必须false |
属性 scrollPaddingEnd | 滚动到元素时应用于虚拟器末尾的填充(以像素为单位) | 类型number | 默认值17 | 必须false |
属性 gap | 虚拟化列表中项目之间的间距 | 类型number | 默认值- | 必须false |
属性 useAnimationFrameWithResizeObserver | 将 ResizeObserver 封装在 requestAnimationFrame 中,实现更顺畅的更新并减少布局抖动 | 类型boolean | 默认值false | 必须false |