Components

选择

显示一个选项列表供用户选择——由一个按钮触发。

import * as React from "react";
import { Select } from "radix-ui";
import classnames from "classnames";
import {
CheckIcon,
ChevronDownIcon,
ChevronUpIcon,
} from "@radix-ui/react-icons";
import "./styles.css";
const SelectDemo = () => (
<Select.Root>
<Select.Trigger className="SelectTrigger" aria-label="Food">
<Select.Value placeholder="Select a fruit…" />
<Select.Icon className="SelectIcon">
<ChevronDownIcon />
</Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Content className="SelectContent">
<Select.ScrollUpButton className="SelectScrollButton">
<ChevronUpIcon />
</Select.ScrollUpButton>
<Select.Viewport className="SelectViewport">
<Select.Group>
<Select.Label className="SelectLabel">Fruits</Select.Label>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes">Grapes</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
</Select.Group>
<Select.Separator className="SelectSeparator" />
<Select.Group>
<Select.Label className="SelectLabel">Vegetables</Select.Label>
<SelectItem value="aubergine">Aubergine</SelectItem>
<SelectItem value="broccoli">Broccoli</SelectItem>
<SelectItem value="carrot" disabled>
Carrot
</SelectItem>
<SelectItem value="courgette">Courgette</SelectItem>
<SelectItem value="leek">Leek</SelectItem>
</Select.Group>
<Select.Separator className="SelectSeparator" />
<Select.Group>
<Select.Label className="SelectLabel">Meat</Select.Label>
<SelectItem value="beef">Beef</SelectItem>
<SelectItem value="chicken">Chicken</SelectItem>
<SelectItem value="lamb">Lamb</SelectItem>
<SelectItem value="pork">Pork</SelectItem>
</Select.Group>
</Select.Viewport>
<Select.ScrollDownButton className="SelectScrollButton">
<ChevronDownIcon />
</Select.ScrollDownButton>
</Select.Content>
</Select.Portal>
</Select.Root>
);
const SelectItem = React.forwardRef(
({ children, className, ...props }, forwardedRef) => {
return (
<Select.Item className={classnames("SelectItem", className)} {...props} ref={forwardedRef} >
<Select.ItemText>{children}</Select.ItemText>
<Select.ItemIndicator className="SelectItemIndicator">
<CheckIcon />
</Select.ItemIndicator>
</Select.Item>
);
},
);
export default SelectDemo;

Features

    可以是受控或非受控。

    提供2种定位模式。

    支持项目、标签和项目组。

    焦点完全管理。

    完整的键盘导航。

    支持自定义占位符。

    支持类型预测。

    支持从右到左的方向。

安装

从命令行安装组件。

npm install @radix-ui/react-select

结构

导入所有部分并组合它们。

import { Select } from "radix-ui";
export default () => (
<Select.Root>
<Select.Trigger>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.ScrollUpButton />
<Select.Viewport>
<Select.Item>
<Select.ItemText />
<Select.ItemIndicator />
</Select.Item>
<Select.Group>
<Select.Label />
<Select.Item>
<Select.ItemText />
<Select.ItemIndicator />
</Select.Item>
</Select.Group>
<Select.Separator />
</Select.Viewport>
<Select.ScrollDownButton />
<Select.Arrow />
</Select.Content>
</Select.Portal>
</Select.Root>
);

API 参考

根元素

包含选择的所有部分。

PropTypeDefault
defaultValue
string
No default value
value
string
No default value
onValueChange
function
No default value
defaultOpen
boolean
No default value
open
boolean
No default value
onOpenChange
function
No default value
dir
enum
No default value
name
string
No default value
disabled
boolean
No default value
required
boolean
No default value

触发器

切换选择的按钮。Select.Content 将通过与触发器对齐来定位自己。

PropTypeDefault
asChild
boolean
false
Data attributeValues
[data-state]"打开" | "关闭"
[data-disabled]

禁用时存在

[data-placeholder]

有占位符时存在

反映所选值的部分。默认情况下,将渲染所选项目的文本。如果您需要更多控制,可以控制选择并传递自己的 children。它不应被样式化以确保正确定位。也可以提供一个可选的 placeholder 属性,以便选择没有值时使用。

PropTypeDefault
asChild
boolean
false
placeholder
ReactNode
No default value

图标

通常显示在值旁边的小图标,为其可打开的事实提供视觉提示。默认渲染为 ▼,但您可以通过 asChild 使用您自己的图标,或使用 children

PropTypeDefault
asChild
boolean
false

门户

使用时,将内容部分传送到 body

PropTypeDefault
container
HTMLElement
document.body

内容

选择打开时弹出的组件。

PropTypeDefault
asChild
boolean
false
onCloseAutoFocus
function
No default value
onEscapeKeyDown
function
No default value
onPointerDownOutside
function
No default value
position
enum
"item-aligned"
side
enum
"bottom"
sideOffset
number
0
align
enum
"start"
alignOffset
number
0
avoidCollisions
boolean
true
collisionBoundary
Boundary
[]
collisionPadding
number | Padding
10
arrowPadding
number
0
sticky
enum
"partial"
hideWhenDetached
boolean
false
Data attributeValues
[data-state]"打开" | "关闭"
[data-side]"左" | "右" | "底部" | "顶部"
[data-align]"start" | "end" | "center"
CSS VariableDescription
--radix-select-content-transform-origin根据内容和箭头的位置/偏移计算出的 transform-origin。仅在 position="popper" 时存在。
--radix-select-content-available-width触发器和边界边缘之间的剩余宽度。仅在 position="popper" 时存在。
--radix-select-content-available-height触发器和边界边缘之间的剩余高度。仅在 position="popper" 时存在。
--radix-select-trigger-width触发器的宽度。仅在 position="popper" 时存在。
--radix-select-trigger-height触发器的高度。仅在 position="popper" 时存在。

视口

包含所有项目的滚动视口。

PropTypeDefault
asChild
boolean
false

项目

包含选择项目的组件。

PropTypeDefault
asChild
boolean
false
value*
string
No default value
disabled
boolean
No default value
textValue
string
No default value
Data attributeValues
[data-state]"checked" | "unchecked"
[data-highlighted]

突出显示时存在

[data-disabled]

禁用时存在

项目文本

项目的文本部分。它应只包含您希望在选择该项目时显示在触发器中的文本。它不应被样式化以确保正确定位。

PropTypeDefault
asChild
boolean
false

项目指示器

在选择该项目时渲染。您可以直接对该元素进行样式设置,或者将其用作放置图标的包装器,或两者皆有。

PropTypeDefault
asChild
boolean
false

向上滚动按钮

可选按钮,用作视觉提示,显示视口溢出并功能性地启用向上滚动。

PropTypeDefault
asChild
boolean
false

向下滚动按钮

可选按钮,用作视觉提示,显示视口溢出并功能性地启用向下滚动。

PropTypeDefault
asChild
boolean
false

用于对多个项目进行分组。与 Select.Label 一起使用,以确保通过自动标签提供良好的可访问性。

PropTypeDefault
asChild
boolean
false

标签

用于渲染组的标签。使用箭头键无法获取焦点。

PropTypeDefault
asChild
boolean
false

分隔符

用于在选择中视觉上分隔项目。

PropTypeDefault
asChild
boolean
false

箭头

可选的箭头元素,渲染在内容旁边。这可以用来帮助视觉上将触发器与 Select.Content 连接。必须在 Select.Content 中渲染。仅在 position 设置为 popper 时可用。

PropTypeDefault
asChild
boolean
false
width
number
10
height
number
5

示例

更改定位模式

默认情况下,Select 的行为类似于原生 MacOS 菜单,通过相对于活动项目来定位 Select.Content。如果您更喜欢类似于 PopoverDropdownMenu 的替代定位方法,那么您可以将 position 设置为 popper,并使用其他对齐选项,例如 sidesideOffset 等。

// index.jsx
import { Select } from "radix-ui";
export default () => (
<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content position="popper" sideOffset={5}>
</Select.Content>
</Select.Portal>
</Select.Root>
);

限制内容大小

Select.Content 上使用 position="popper" 时,您可能希望限制内容的宽度,以便与触发器的宽度匹配。您也可能想限制它的高度,以免超过视口。

我们暴露了几个 CSS 自定义属性,例如 --radix-select-trigger-width--radix-select-content-available-height 以支持此操作。使用它们来限制内容的维度。

// index.jsx
import { Select } from "radix-ui";
import "./styles.css";
export default () => (
<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content className="SelectContent" position="popper" sideOffset={5} >
</Select.Content>
</Select.Portal>
</Select.Root>
);
/* styles.css */
.SelectContent {
width: var(--radix-select-trigger-width);
max-height: var(--radix-select-content-available-height);
}

带有禁用项目

您可以通过 data-disabled 属性为禁用的项目添加特殊样式。

// index.jsx
import { Select } from "radix-ui";
import "./styles.css";
export default () => (
<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Item className="SelectItem" disabled>
</Select.Item>
<Select.Item></Select.Item>
<Select.Item></Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
);
/* styles.css */
.SelectItem[data-disabled] {
color: "gainsboro";
}

带有占位符

您可以在 Value 上使用 placeholder 属性,以便在选择没有值时使用。Trigger 上也有一个 data-placeholder 属性,以帮助进行样式设置。

// index.jsx
import { Select } from "radix-ui";
import "./styles.css";
export default () => (
<Select.Root>
<Select.Trigger className="SelectTrigger">
<Select.Value placeholder="选择一个选项" />
<Select.Icon />
</Select.Trigger>
<Select.Portal>
<Select.Content></Select.Content>
</Select.Portal>
</Select.Root>
);
/* styles.css */
.SelectTrigger[data-placeholder] {
color: "gainsboro";
}

带分隔符

使用 Separator 部分在项目之间添加分隔符。

<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Item></Select.Item>
<Select.Item></Select.Item>
<Select.Item></Select.Item>
<Select.Separator />
<Select.Item></Select.Item>
<Select.Item></Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>

带有分组项目

使用 GroupLabel 部分在一个部分中对项目进行分组。

<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Group>
<Select.Label>标签</Select.Label>
<Select.Item></Select.Item>
<Select.Item></Select.Item>
<Select.Item></Select.Item>
</Select.Group>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>

带有复杂项目

您可以在项目中使用自定义内容。

import { Select } from "radix-ui";
export default () => (
<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Item>
<Select.ItemText>
<img src="" />
阿道夫·赫斯
</Select.ItemText>
<Select.ItemIndicator></Select.ItemIndicator>
</Select.Item>
<Select.Item></Select.Item>
<Select.Item></Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
);

控制触发器中显示的值

默认情况下,触发器将自动显示所选项目 ItemText 的内容。您可以通过选择将内容放入/放出 ItemText 部分来控制显示的内容。

如果您需要更多灵活性,可以使用 value/onValueChange 属性控制组件,并将 children 传递给 SelectValue。请确保您放入的内容是可访问的。

const countries = { france: "🇫🇷", "united-kingdom": "🇬🇧", spain: "🇪🇸" };
export default () => {
const [value, setValue] = React.useState("france");
return (
<Select.Root value={value} onValueChange={setValue}>
<Select.Trigger>
<Select.Value aria-label={value}>
{countries[value]}
</Select.Value>
<Select.Icon />
</Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Item value="france">
<Select.ItemText>法国</Select.ItemText>
<Select.ItemIndicator></Select.ItemIndicator>
</Select.Item>
<Select.Item value="united-kingdom">
<Select.ItemText>英国</Select.ItemText>
<Select.ItemIndicator></Select.ItemIndicator>
</Select.Item>
<Select.Item value="spain">
<Select.ItemText>西班牙</Select.ItemText>
<Select.ItemIndicator></Select.ItemIndicator>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
);
};

带有自定义滚动条

默认情况下,原生滚动条是隐藏的,因为我们推荐使用 ScrollUpButtonScrollDownButton 部分以获得最佳用户体验。如果您不想使用这些部分,请将选择与我们的 滚动区域 原语组合使用。

// index.jsx
import { Select, ScrollArea } from "radix-ui";
import "./styles.css";
export default () => (
<Select.Root>
<Select.Trigger></Select.Trigger>
<Select.Portal>
<Select.Content>
<ScrollArea.Root className="ScrollAreaRoot" type="auto">
<Select.Viewport asChild>
<ScrollArea.Viewport className="ScrollAreaViewport">
<StyledItem></StyledItem>
<StyledItem></StyledItem>
<StyledItem></StyledItem>
</ScrollArea.Viewport>
</Select.Viewport>
<ScrollArea.Scrollbar className="ScrollAreaScrollbar" orientation="vertical" >
<ScrollArea.Thumb className="ScrollAreaThumb" />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
</Select.Content>
</Select.Portal>
</Select.Root>
);
/* styles.css */
.ScrollAreaRoot {
width: 100%;
height: 100%;
}
.ScrollAreaViewport {
width: 100%;
height: 100%;
}
.ScrollAreaScrollbar {
width: 4px;
padding: 5px 2px;
}
.ScrollAreaThumb {
background: rgba(0, 0, 0, 0.3);
border-radius: 3px;
}

可访问性

遵循 ListBox WAI-ARIA 设计模式

查看 W3C 选择仅组合框 示例以获取更多信息。

键盘交互

KeyDescription
空格
当焦点在 Select.Trigger 上时,打开选择并聚焦所选项目。
当焦点在项目上时,选择该项目。
回车
当焦点在 Select.Trigger 上时,打开选择并聚焦第一个项目。
当焦点在项目上时,选择该项目。
箭头向下
当焦点在 Select.Trigger 上时,打开选择。
当焦点在项目上时,将焦点移动到下一个项目。
箭头向上
当焦点在 Select.Trigger 上时,打开选择。
当焦点在项目上时,将焦点移动到上一个项目。
Esc
关闭选择并将焦点移到 Select.Trigger

标签

使用我们的 标签 组件为选择提供视觉和可访问的标签。

import { Select, Label } from "radix-ui";
export default () => (
<>
<Label>
国家
<Select.Root></Select.Root>
</Label>
{/* 或 */}
<Label htmlFor="country">国家</Label>
<Select.Root>
<Select.Trigger id="country"></Select.Trigger>
<Select.Portal>
<Select.Content></Select.Content>
</Select.Portal>
</Select.Root>
</>
);

自定义 API

通过将原语部分抽象到自己的组件中创建自己的 API。

抽象到 SelectSelectItem

此示例抽象了大部分部分。

用法

import { Select, SelectItem } from "./your-select";
export default () => (
<Select defaultValue="2">
<SelectItem value="1">项目 1</SelectItem>
<SelectItem value="2">项目 2</SelectItem>
<SelectItem value="3">项目 3</SelectItem>
</Select>
);

实现

// your-select.jsx
import * as React from "react";
import { Select as SelectPrimitive } from "radix-ui";
import {
CheckIcon,
ChevronDownIcon,
ChevronUpIcon,
} from "@radix-ui/react-icons";
export const Select = React.forwardRef(
({ children, ...props }, forwardedRef) => {
return (
<SelectPrimitive.Root {...props}>
<SelectPrimitive.Trigger ref={forwardedRef}>
<SelectPrimitive.Value />
<SelectPrimitive.Icon>
<ChevronDownIcon />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
<SelectPrimitive.Portal>
<SelectPrimitive.Content>
<SelectPrimitive.ScrollUpButton>
<ChevronUpIcon />
</SelectPrimitive.ScrollUpButton>
<SelectPrimitive.Viewport>{children}</SelectPrimitive.Viewport>
<SelectPrimitive.ScrollDownButton>
<ChevronDownIcon />
</SelectPrimitive.ScrollDownButton>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
</SelectPrimitive.Root>
);
},
);
export const SelectItem = React.forwardRef(
({ children, ...props }, forwardedRef) => {
return (
<SelectPrimitive.Item {...props} ref={forwardedRef}>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
<SelectPrimitive.ItemIndicator>
<CheckIcon />
</SelectPrimitive.ItemIndicator>
</SelectPrimitive.Item>
);
},
);