SegmentedControl(分段控件)在几个选项之间单选,比如「列表 / 网格」视图切换或外观设置。它像 radio 组一样工作:只有一个值、一个 Tab 停靠点,选中指示器滑到所选项背后。选项对应互斥的内容面板时用标签页;分段控件选择的是值本身,常参与表单提交。
组合
SegmentedControl
├── SegmentedControlIndicator
├── SegmentedControlItem
└── SegmentedControlItem每个 SegmentedControlItem 要有唯一的 value,控件的值就是选中项的 value,指示器停在它背后。用 aria-label 命名控件,或放进带标签的 Field。不写 defaultValue 时初始没有选中项,Tab 落在第一个可用项上。
用法
import {
SegmentedControl,
SegmentedControlIndicator,
SegmentedControlItem,
} from "@ui-sdk/react/segmented-control";
function Example() {
return (
<SegmentedControl aria-label="视图" defaultValue="list">
<SegmentedControlIndicator />
<SegmentedControlItem value="list">列表</SegmentedControlItem>
<SegmentedControlItem value="grid">网格</SegmentedControlItem>
<SegmentedControlItem value="board">看板</SegmentedControlItem>
</SegmentedControl>
);
}orientation="vertical" 竖排选项;无论哪个方向,两轴的方向键都能在项间移动,与原生 radio 组一致。loop(默认 true)让方向键从末项绕回首项,设为 false 则停在两端。
指示器
SegmentedControlIndicator 测量选中项的位置与尺寸,写进自己的 CSS 变量;项或控件尺寸变化时重新测量,没有选中项时不产生盒子。把它放在控件内部,用变量画出滑动的背景:
[data-slot="segmented-control"] {
position: relative;
display: inline-flex;
isolation: isolate;
}
[data-slot="segmented-control-indicator"] {
position: absolute;
z-index: -1;
top: var(--segmented-control-indicator-top);
left: var(--segmented-control-indicator-left);
width: var(--segmented-control-indicator-width);
height: var(--segmented-control-indicator-height);
border-radius: 0.375rem;
background: white;
box-shadow: 0 1px 2px rgb(0 0 0 / 12%);
transition-property: top, left, width, height;
transition-duration: 200ms;
}另有 --segmented-control-indicator-right / -bottom 给出到控件右、下边缘的距离,RTL 布局用它们代替 left。
表单
控件为每个项渲染一个隐藏的原生 radio 参与表单:name 是提交用的字段名(不写则不提交任何值),required 在未选中时用浏览器原生提示阻止提交,form 让它归属页面里指定 id 的表单。表单重置回到 defaultValue。readOnly 保住值并照常提交,项仍可聚焦,方向键只移动焦点不改选中。
<form>
<SegmentedControl name="theme" defaultValue="system" required>
<SegmentedControlIndicator />
<SegmentedControlItem value="light">浅色</SegmentedControlItem>
<SegmentedControlItem value="dark">深色</SegmentedControlItem>
<SegmentedControlItem value="system">跟随系统</SegmentedControlItem>
</SegmentedControl>
<button type="submit">保存</button>
<button type="reset">重置</button>
</form>受控与控制器
value 加 onValueChange(value, details) 接管选中值,null 表示都不选。details.reason 有 item-press、list-navigation、reset、restore、imperative,details.cancel() 可拦截。createSegmentedControlController()(来自 @ui-sdk/react/segmented-control)创建的控制器提供 getValue / setValue / focus,setValue 像用户按压一样走变更回调,禁用项与只读控件会忽略它。参见控制器。
const [view, setView] = React.useState<string | null>("list");
<SegmentedControl aria-label="视图" value={view} onValueChange={(next) => setView(next)}>
<SegmentedControlIndicator />
<SegmentedControlItem value="list">列表</SegmentedControlItem>
<SegmentedControlItem value="grid">网格</SegmentedControlItem>
</SegmentedControl>;disabled 禁用全部项:不可聚焦、不可更改、不提交值;单项 disabled 像禁用的原生 radio,方向键跳过它。包裹在禁用的 fieldset 里效果相同。
样式与状态
各部件带 data-slot:segmented-control、segmented-control-item、segmented-control-indicator。控件与项带 data-disabled / data-readonly / data-required,项另有 data-checked / data-unchecked,控件与指示器带 data-orientation。
[data-slot="segmented-control"] {
gap: 2px;
padding: 2px;
border-radius: 0.5rem;
background: rgb(0 0 0 / 5%);
}
[data-slot="segmented-control-item"] {
padding: 0.25rem 0.75rem;
}
[data-slot="segmented-control-item"][data-checked] {
font-weight: 600;
}
[data-slot="segmented-control-item"][data-disabled] {
opacity: 0.4;
}API
根 SegmentedControl 的主要 props:
| Prop | 类型 | 说明 |
|---|---|---|
value |
T | null |
选中项的 value,传入即受控,null 都不选。 |
defaultValue |
T | null |
非受控时初始选中项,表单重置回到它。 |
onValueChange |
(value, details) => void |
值要变化时运行,details.cancel() 可拦截。 |
name |
string |
表单提交的字段名,不写则不提交。 |
form |
string |
归属的表单 id,控件不在该表单内时用。 |
required |
boolean |
未选中时阻止表单提交。 |
disabled |
boolean |
禁用全部项,不可聚焦、不提交。 |
readOnly |
boolean |
可聚焦、照常提交,但不可更改。 |
loop |
boolean |
方向键是否首尾环绕,默认 true。 |
orientation |
"horizontal" | "vertical" |
选项排列方向,默认横向。 |
controller |
object |
用 createSegmentedControlController() 创建。 |
SegmentedControlItem 必填 value,另有 disabled;指示器没有专属 props。
键盘与无障碍
遵循 WAI-ARIA radio 组模式:每个项是带 radio 角色的按钮,外加一个参与表单的隐藏原生 radio;指示器是纯装饰,屏幕阅读器跳过。
Tab进入控件,落在选中项或第一个可用项上,再按即离开,整个控件只有一个停靠点。- 方向键移到相邻项并选中它,跳过禁用项,默认首尾环绕;横竖两轴的方向键都有效。
Space选中聚焦的项;Enter提交所在表单。- 只读时方向键只移动焦点;禁用项不可聚焦。