#Tabs 标签页
在同一区域切换不同内容。
切换时正文和内容区域高度立即更新,不使用过渡动画;标签栏的选中指示线保留移动效果。
卡片样式的标签栏默认随标签内容调整宽度,空间不足时在栏内滚动。size 控制整个标签栏的尺寸,正文区域使用可用宽度。
#基础用法
线形标签
查看项目活动、成员和最近更新。
卡片标签
查看项目活动、成员和最近更新。
纵向标签
查看项目活动、成员和最近更新。
basic.tsx
import type { TabItem } from '@ifhover/leaf-ui';
import { Input, Space, Tabs } from '@ifhover/leaf-ui';
import { ChartNoAxesColumn, Settings } from 'lucide-react';
const items: readonly TabItem[] = [
{
key: 'overview',
label: '概览',
icon: <ChartNoAxesColumn />,
children: '查看项目活动、成员和最近更新。',
},
{
key: 'settings',
label: '设置',
icon: <Settings />,
children: <Input aria-label="项目名称" placeholder="输入内容后切换,内容会保留" />,
},
{ key: 'history', label: '历史', disabled: true, children: null },
];
export function TabsBasic() {
return (
<Space direction="vertical" align="stretch">
<Space direction="vertical" align="stretch" size="sm">
<span>线形标签</span>
<Tabs aria-label="项目信息" items={items} />
</Space>
<Space direction="vertical" align="stretch" size="sm">
<span>卡片标签</span>
<Tabs aria-label="卡片标签" items={items} type="card" />
</Space>
<Space direction="vertical" align="stretch" size="sm">
<span>纵向标签</span>
<Tabs aria-label="纵向标签" items={items} placement="left" />
</Space>
</Space>
);
}
#不同尺寸
线形与卡片标签分别展示小、默认和大尺寸。
线形标签
小尺寸
查看项目的最近更新。
默认尺寸
查看项目的最近更新。
大尺寸
查看项目的最近更新。
卡片标签
小尺寸
查看项目的最近更新。
默认尺寸
查看项目的最近更新。
大尺寸
查看项目的最近更新。
sizes.tsx
import { Grid, Space, Tabs, Text } from '@ifhover/leaf-ui';
const items = [
{ key: 'overview', label: '概览', children: '查看项目的最近更新。' },
{ key: 'members', label: '成员', children: '管理项目成员和访问权限。' },
];
const labels = { sm: '小尺寸', md: '默认尺寸', lg: '大尺寸' };
const variants = [
{ type: 'line', label: '线形标签' },
{ type: 'card', label: '卡片标签' },
] as const;
export function TabsSizes() {
return (
<Grid columns={{ xs: 1, sm: 2 }} gap={24}>
{variants.map(({ type, label }) => (
<Space key={type} direction="vertical" align="stretch" size="lg">
<Text strong>{label}</Text>
{(['sm', 'md', 'lg'] as const).map((size) => (
<Space key={size} direction="vertical" align="stretch" size="sm">
<span>{labels[size]}</span>
<Tabs
aria-label={`${label} · ${labels[size]}`}
items={items}
type={type}
size={size}
/>
</Space>
))}
</Space>
))}
</Grid>
);
}
#可关闭标签页
点击加号新增标签页。全部关闭后组件隐藏,可使用示例中的新增按钮重新打开。
项目一的内容
closable.tsx
import { Button, Space, type TabItem, Tabs } from '@ifhover/leaf-ui';
import { useRef, useState } from 'react';
export function TabsClosable() {
const sequence = useRef(2);
const [items, setItems] = useState<TabItem[]>([
{ key: '1', label: '项目一', children: '项目一的内容', closable: true },
{ key: '2', label: '项目二', children: '项目二的内容', closable: true },
]);
const addTab = () => {
const key = String(++sequence.current);
setItems((list) => [
...list,
{ key, label: `项目 ${key}`, children: `项目 ${key} 的内容`, closable: true },
]);
};
return (
<Space direction="vertical" align="stretch">
<Tabs
aria-label="可关闭标签页"
items={items}
type="card"
onClose={(key) => setItems((list) => list.filter((item) => item.key !== key))}
onAdd={addTab}
addLabel="新增标签页"
/>
{items.length === 0 && (
<Space>
<Button variant="outline" onClick={addTab}>
新增标签页
</Button>
</Space>
)}
</Space>
);
}
#扩展用法
项目 1 内容
extensions.tsx
import { Button, Space, type TabItem, Tabs } from '@ifhover/leaf-ui';
import { useRef, useState } from 'react';
export function ExtensionDemo() {
const count = useRef(7);
const [items, setItems] = useState<TabItem[]>(
Array.from({ length: 6 }, (_, i) => ({
key: String(i),
label: `项目 ${i + 1}`,
children: `项目 ${i + 1} 内容`,
closable: true,
})),
);
const addTab = () => {
const id = String(count.current++);
setItems((current) => [
...current,
{ key: id, label: `项目 ${id}`, children: '新项目', closable: true },
]);
};
return (
<Space direction="vertical" align="stretch">
<Tabs
aria-label="可排序标签页"
items={items}
sortable
onReorder={setItems}
onAdd={addTab}
onClose={(key) => setItems((current) => current.filter((item) => item.key !== key))}
/>
{items.length === 0 && (
<Space>
<Button variant="outline" onClick={addTab}>
新增标签页
</Button>
</Space>
)}
</Space>
);
}
#底部与右侧
底部标签页
Design settings
右侧标签页
Design settings
capabilities.tsx
import { Space, Tabs } from '@ifhover/leaf-ui';
const items = [
{ key: 'design', label: 'Design', children: 'Design settings' },
{ key: 'tokens', label: 'Tokens', children: 'Theme tokens' },
];
export function Capabilities() {
return (
<Space direction="vertical" align="stretch">
<Space direction="vertical" align="stretch" size="sm">
<span>{'底部标签页'}</span>
<Tabs
aria-label={'底部标签页'}
placement="bottom"
items={items}
/>
</Space>
<Space direction="vertical" align="stretch" size="sm">
<span>{'右侧标签页'}</span>
<Tabs aria-label={'右侧标签页'} placement="right" items={items} />
</Space>
</Space>
);
}
#Tabs API
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
items | readonly TabItem[] | 必填 | 标签页列表;为空时不渲染组件 |
activeKey | string | 首个可用项 | 受控激活项 / 初始项 |
defaultActiveKey | string | 首个可用项 | 受控激活项 / 初始项 |
onChange | (key: string) => void | — | 激活项变化 |
type | 'line' | 'card' | 'line' | 标签形态 |
placement | 'top' | 'bottom' | 'left' | 'right' | 'top' | 标签栏位置 |
size | 'sm' | 'md' | 'lg' | 'md' | 标签尺寸;卡片样式包含标签栏的内边距和边框 |
activationMode | 'automatic' | 'manual' | 'automatic' | 方向键直接激活 / 回车或空格激活 |
destroyInactive | boolean | false | 切走时卸载内容 |
extra | ReactNode | — | 标签栏附加内容 |
onClose | (key: string, event: MouseEvent<HTMLButtonElement>) => void | — | 请求关闭,由应用移除 items 中的项;可阻止默认切换 |
onAdd | () => void | — | 添加页签,传入后显示添加按钮 |
addLabel | string | — | 添加按钮的可访问名称 |
overflow | boolean | true | 溢出页签收进更多菜单 |
sortable | boolean | false | 启用排序,配合 onReorder 使用 |
onReorder | (items: TabItem[]) => void | — | 返回排序后的页签 |
#TabItem
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
key | string | 必填 | 必需。唯一标识 |
label | ReactNode | 必填 | 必需。标签名称 |
children | ReactNode | 必填 | 必需。对应内容 |
disabled | boolean | false | 禁用交互。 |
icon | ReactNode | — | 前置图标 |
closable | boolean | false | 展示关闭按钮,需配合 onClose |
#类型
定义组件属性或组装列表时可导入相关类型。
import type { TabsProps, TabItem } from '@ifhover/leaf-ui';#使用说明
内容首次访问时挂载,默认保留访问过的内容和输入状态。方向键会跳过禁用项,Home / End 跳至首尾;manual 模式适合内容切换需要等待的场景。