index.md 22 KB


localeCode: zh-CN order: 42 category: 导航类 title: Tabs 标签栏 icon: doc-tabs

brief: 当内容需要分组并在不同模块页面中展示,可使用 Tabs 标签栏目对不同的组/页之间进行切换

代码演示

如何引入

import { Tabs, TabPane } from '@douyinfe/semi-ui';

基本用法

标签栏支持三种样式的显示:线条式,按钮式,卡片式。默认选中第一项。
标签页支持两种传入方式:通过 tabList 传入标签页对象的数组,或使用 <TabPane> 逐项显式传入。
两种方式建议不要同时使用,同时使用时会优先渲染通过 tabList 传入的数据。

当使用 tabList 时每次只渲染当前传入的节点使用 <TabPane> 时默认会渲染所有面板。可以通过设置 keepDOM={false} 只渲染当前面板,此时不会有动画效果。

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';

() => (
    <div>
        <Tabs type="line">
            <TabPane tab="文档" itemKey="1">
                <h3>文档</h3>
                <p style={{ lineHeight: 1.8 }}>
                    Semi Design 是由互娱社区前端团队与 UED
                    团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的
                    Web 应用。
                </p>
                <p style={{ lineHeight: 1.8 }}>
                    区别于其他的设计系统而言,Semi Design
                    以用户中心、内容优先、设计人性化为设计理念,具有以下优势:
                </p>
                <ul>
                    <li>
                        <p>Semi Design 以内容优先进行设计。</p>
                    </li>
                    <li>
                        <p>更容易地自定义主题。</p>
                    </li>
                    <li>
                        <p>适用国际化场景。</p>
                    </li>
                    <li>
                        <p>效率场景加入人性化关怀。</p>
                    </li>
                </ul>
            </TabPane>
            <TabPane tab="快速起步" itemKey="2">
                <h3>快速起步</h3>
                <pre
                    style={{
                        margin: '24px 0',
                        padding: '20px',
                        border: 'none',
                        whiteSpace: 'normal',
                        borderRadius: 'var(--semi-border-radius-medium)',
                        color: 'var(--semi-color-text-1)',
                        backgroundColor: 'var(--semi-color-fill-0)',
                    }}
                >
                    <code>
                        yarn add @douyinfe/semi-ui
                    </code>
                </pre>
            </TabPane>
            <TabPane tab="帮助" itemKey="3">
                <h3>帮助</h3>
                <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-0)', fontWeight: 600 }}>
                    Q:有新组件需求、或者现有组件feature不能满足业务需求?
                </p>
                <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-1)' }}>
                    右上角问题反馈,提交issue,label选择Feature Request / New Component Request
                    我们会高优处理这些需求。
                </p>
                <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-0)', fontWeight: 600 }}>
                    Q:对组件的使用有疑惑?
                </p>
                <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-1)' }}>
                    欢迎进我们的客服lark群进行咨询提问。
                </p>
            </TabPane>
        </Tabs>
    </div>
);

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';

() => (
    <Tabs type="button">
        <TabPane tab="文档" itemKey="1">
            文档
        </TabPane>
        <TabPane tab="快速起步" itemKey="2">
            快速起步
        </TabPane>
        <TabPane tab="帮助" itemKey="3">
            帮助
        </TabPane>
    </Tabs>
);
import React from 'react';
import { Tabs } from '@douyinfe/semi-ui';

class TabDemo extends React.Component {
    constructor() {
        super();
        this.state = { key: '1' };
        this.onTabClick = this.onTabClick.bind(this);
    }

    onTabClick(key, type) {
        this.setState({ [type]: key });
    }

    render() {
        // eslint-disable-next-line react/jsx-key
        const contentList = [<div>文档</div>, <div>快速起步</div>, <div>帮助</div>];
        const tabList = [
            { tab: '文档', itemKey: '1' },
            { tab: '快速起步', itemKey: '2' },
            { tab: '帮助', itemKey: '3' },
        ];
        return (
            <Tabs
                type="card"
                tabList={tabList}
                onChange={key => {
                    this.onTabClick(key, 'key');
                }}
            >
                {contentList[this.state.key - 1]}
            </Tabs>
        );
    }
}

带图标的

有图标的标签栏。

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';
import { IconFile, IconGlobe, IconHelpCircle } from '@douyinfe/semi-icons';

() => (
    <Tabs>
        <TabPane
            tab={
                <span>
                    <IconFile />
                    文档
                </span>
            }
            itemKey="1"
        >
            文档
        </TabPane>
        <TabPane
            tab={
                <span>
                    <IconGlobe />
                    快速起步
                </span>
            }
            itemKey="2"
        >
            快速起步
        </TabPane>
        <TabPane
            tab={
                <span>
                    <IconHelpCircle />
                    帮助
                </span>
            }
            itemKey="3"
        >
            帮助
        </TabPane>
    </Tabs>
);

垂直的标签栏

支持水平和垂直两种模式, tabPosition='left|top'

import React from 'react';
import { Tabs, TabPane, RadioGroup, Radio } from '@douyinfe/semi-ui';
import { IconFile, IconGlobe, IconHelpCircle } from '@douyinfe/semi-icons';

class App extends React.Component {
    constructor() {
        super();
        this.state = {
            type: 'line',
        };
    }

    onSelect(e) {
        this.setState({
            type: e.target.value,
        });
    }

    render() {
        return (
            <>
                <RadioGroup
                    onChange={e => this.onSelect(e)}
                    value={this.state.type}
                    style={{
                        display: 'flex',
                        justifyContent: 'center',
                    }}
                >
                    <Radio value={'line'}>Line</Radio>
                    <Radio value={'card'}>Card</Radio>
                    <Radio value={'button'}>Button</Radio>
                </RadioGroup>
                <br />
                <br />
                <Tabs tabPosition="left" type={this.state.type}>
                    <TabPane
                        tab={
                            <span>
                                <IconFile />
                                文档
                            </span>
                        }
                        itemKey="1"
                    >
                        <div style={{ padding: '0 24px' }}>
                            <h3>文档</h3>
                            <p style={{ lineHeight: 1.8 }}>
                                Semi Design 是由互娱社区前端团队与 UED
                                团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的
                                Web 应用。
                            </p>
                            <p style={{ lineHeight: 1.8 }}>
                                区别于其他的设计系统而言,Semi Design
                                以用户中心、内容优先、设计人性化为设计理念,具有以下优势:
                            </p>
                        </div>
                    </TabPane>
                    <TabPane
                        tab={
                            <span>
                                <IconGlobe />
                                快速起步
                            </span>
                        }
                        itemKey="2"
                    >
                        <div style={{ padding: '0 24px' }}>
                            <h3>快速起步</h3>
                            <pre
                                style={{
                                    margin: '24px 0',
                                    padding: '20px',
                                    border: 'none',
                                    whiteSpace: 'normal',
                                    borderRadius: '6px',
                                    color: 'var(--semi-color-text-1)',
                                    backgroundColor: 'var(--semi-color-fill-0)',
                                }}
                            >
                                <code>
                                    yarn add @douyinfe/semi-ui
                                </code>
                            </pre>
                        </div>
                    </TabPane>
                    <TabPane
                        tab={
                            <span>
                                <IconHelpCircle />
                                帮助
                            </span>
                        }
                        itemKey="3"
                    >
                        <div style={{ padding: '0 24px' }}>
                            <h3>帮助</h3>
                            <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-0)', fontWeight: 600 }}>
                                Q:有新组件需求、或者现有组件feature不能满足业务需求?
                            </p>
                            <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-1)' }}>
                                右上角问题反馈,提交issue,label选择Feature Request / New Component Request
                                我们会高优处理这些需求。
                            </p>
                            <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-0)', fontWeight: 600 }}>
                                Q:对组件的使用有疑惑?
                            </p>
                            <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-1)' }}>
                                欢迎进我们的客服lark群进行咨询提问。
                            </p>
                        </div>
                    </TabPane>
                </Tabs>
            </>
        );
    }
}

滚动折叠

v>= 1.1.0
通过设置 collapsible 可以支持滚动折叠,目前只支持 horizontal 模式。

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';

class App extends React.Component {
    render() {
        return (
            <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible>
                {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
                    <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
                        Content of card tab {i}
                    </TabPane>
                ))}
            </Tabs>
        );
    }
}

禁用

禁用标签栏中的某一个标签页。

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';

class App extends React.Component {
    render() {
        return (
            <Tabs defaultActiveKey="1">
                <TabPane tab="文档" itemKey="1">
                    文档
                </TabPane>
                <TabPane tab="快速起步" itemKey="2" disabled>
                    快速起步
                </TabPane>
                <TabPane tab="帮助" itemKey="3">
                    帮助
                </TabPane>
            </Tabs>
        );
    }
}

标签栏内容扩展

传入 tabBarExtraContent 属性可以在标签栏右侧添加附加操作。

import React from 'react';
import { Tabs, TabPane, Button } from '@douyinfe/semi-ui';

() => (
    <Tabs
        defaultActiveKey="1"
        tabBarExtraContent={
            <Button
                onClick={() => {
                    alert('you have clicked me!');
                }}
            >
                Extra Action
            </Button>
        }
    >
        <TabPane tab="文档" itemKey="1">
            文档
        </TabPane>
        <TabPane tab="快速起步" itemKey="2">
            快速起步
        </TabPane>
        <TabPane tab="帮助" itemKey="3">
            帮助
        </TabPane>
    </Tabs>
);

标签栏二次封装

传入 renderTabBar 函数可对标签栏进行二次封装。

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';

() => (
    <Tabs
        defaultActiveKey="1"
        renderTabBar={(tabBarProps, DefaultTabBar) => {
            return (
                <div className="tab-bar-box">
                    这是二次封装的Tab Bar,当前ActiveKey:{tabBarProps.activeKey}
                    <DefaultTabBar {...tabBarProps} />
                </div>
            );
        }}
    >
        <TabPane tab="文档" itemKey="1">
            文档
        </TabPane>
        <TabPane tab="快速起步" itemKey="2">
            快速起步
        </TabPane>
        <TabPane tab="帮助" itemKey="3">
            帮助
        </TabPane>
    </Tabs>
);

动态更新

通过绑定事件,可以使标签栏动态更新。

import React from 'react';
import { Tabs, TabPane, ButtonGroup, Button } from '@douyinfe/semi-ui';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.newTabIndex = 0;
        const panes = [
            { title: 'Tab 1', content: 'Content of Tab Pane 1', itemKey: '1' },
            { title: 'Tab 2', content: 'Content of Tab Pane 2', itemKey: '2' },
        ];
        this.state = {
            panes,
            activeKey: panes[0].itemKey,
        };
    }

    add() {
        const { panes } = this.state;
        const index = this.newTabIndex++;
        panes.push({ title: `New Tab ${index}`, content: 'New Tab Pane', itemKey: `newTab${index}` });
        this.setState({ panes, activeKey: `newTab${index}` });
    }

    remove() {
        const { panes } = this.state;
        if (panes.length > 1) {
            panes.pop();
            this.setState({ panes, activeKey: panes[panes.length - 1].itemKey });
        }
    }

    handleChange(activeKey) {
        this.setState({ activeKey });
    }

    render() {
        const { panes, activeKey } = this.state;
        return (
            <Tabs
                defaultActiveKey="1"
                activeKey={activeKey}
                onChange={this.handleChange.bind(this)}
                tabBarExtraContent={
                    <ButtonGroup>
                        <Button onClick={() => this.add()}>新增</Button>
                        <Button onClick={() => this.remove()}>删除</Button>
                    </ButtonGroup>
                }
            >
                {panes.map(pane => (
                    <TabPane tab={pane.title} itemKey={pane.itemKey} key={pane.itemKey}>
                        {pane.content}
                    </TabPane>
                ))}
            </Tabs>
        );
    }
}

关闭

关闭标签栏中的某一个标签页。
只有卡片样式的页签支持关闭选项。使用 closable={true} 来开启。

import React from 'react';
import { Tabs, TabPane } from '@douyinfe/semi-ui';

class App extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            tabList: [
                { tab: '文档', itemKey: '1', text: '文档', closable: true },
                { tab: '快速起步', itemKey: '2', text: '快速起步', closable: true },
                { tab: '帮助', itemKey: '3', text: '帮助' },
            ]
        };
    }
    close(key){
        const newTabList = [...this.state.tabList];
        const closeIndex = newTabList.findIndex(t=>t.itemKey===key);
        newTabList.splice(closeIndex, 1);
        this.setState({ tabList: newTabList });
    }
    render() {
        return (
            <Tabs type="card" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
                {
                    this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>{t.text}</TabPane>)
                }
            </Tabs>
        );
    }
}

API 参考

Tab

属性 说明 类型 默认值
activeKey 当前激活的 tab 页的 itemKey 值 string
className 类名 string
collapsible 折叠的 Tabs,>=1.1.0 boolean false
contentStyle 内容区域外层样式对象 CSSProperties
defaultActiveKey 初始化选中的 tab 页的 key 值 string '1'
keepDOM 使用 TabPane 写法时是否渲染隐藏面板的 DOM 结构,>=1.0.0 boolean true
lazyRender 懒渲染,仅当面板激活过才被渲染在 DOM 树中, >=1.0.0 boolean false
renderTabBar 用于二次封装标签栏 (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode
preventScroll 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 boolean
size 大小,提供 largemediumsmall 三种类型,>=1.11.0,目前仅支持线性 Tabs string large
style 样式对象 CSSProperties
tabBarExtraContent 用于扩展标签栏的内容 ReactNode
tabList 标签页对象组成的数组,该对象支持 itemKey(对应 activeKey,tab(标签页文字)及 icon(标签页图标) TabPane[]
tabPaneMotion 是否使用动画切换 tabs boolean true
tabPosition tab 的位置,支持top(水平), left(垂直),>=1.0.0 boolean top
type 标签栏的样式,可选linecardbutton string line
onChange 切换 tab 页时的回调函数 function(activeKey: string)
onTabClick 单击事件 function(key: string, e: Event)
onTabClose 关闭 tab 页时的回调函数 >=2.1.0 function(tabKey: string)

TabPane

属性 说明 类型 默认值
className 类名 string
disabled 标签页栏是否禁用 boolean
icon 标签页栏 icon ReactNode
itemKey 对应 activeKey string
style 样式对象 CSSProperties
tab 标签页栏显示文字 ReactNode
closable 允许关闭tab >=2.1.0 boolean false

Accessibility

ARIA

  • 关于 role
    • TabBar 对应的 role 为 tablist
    • TabBar 中的 Tab 对应的 role 为 tab
    • TabPane 对应的 role 为 tabpanel
  • aria-orientation: 表明 TabBar 的方向,有 verticalhorizontal 两种。当传入 tabPosition 为 left 时, aria-orientation 会被设置为 vertical,tabPosition 为 top 时,设置为 horizontal
  • aria-disabled: 当 TabPane 设置为 disabled 时,对应 Tab 的 aria-disabled 会被设置为 true
  • aria-selected: 表明 Tab 是否被选中
  • aria-controls: 指向 Tab 标签所控制的 TabPane
  • aria-labelledby: 指向设置 TabPane 标签的元素

键盘和焦点

WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/

  • 选项卡可以被获取到焦点,但禁用的选项卡除外
  • 键盘用户可以使用 Tab 键,将焦点移动到已被选择的选项卡元素的选项卡面板上
  • 当焦点位于水平选项卡列表中的选项卡元素上时,使用 左右箭头 来切换选项
  • 当焦点位于垂直选项卡列表中的选项卡元素上时,使用 上下箭头 来切换选项
  • 当焦点位于选项卡列表中的未被激活的选项卡元素上时,可以使用 SpaceEnter 键来激活该选项卡
  • 当键盘用户想要直接将焦点聚焦到选项卡列表中的最后一个选项卡元素时:
    • Mac 用户:fn + 右箭头
    • Windows 用户:End
  • 当键盘用户想要直接将焦点聚焦到选项卡列表中的第一个选项卡元素时:
    • Mac 用户:fn + 左箭头
    • Windows 用户:Home
  • 当选项卡允许被删除时:
    • 用户可以使用 Delete 键删除选项卡
    • 删除后,焦点转移到被删除选项卡元素的后一个元素上;若被删除元素无后一个元素则转移到前一个元素上

设计变量

文案规范

  • 标签文案需要准确清晰地解释标签内容
  • 用简短的,易区分的标签
  • 尽量保持在一个词以内

FAQ

  • 为什么在Tabs中使用 Typography 的省略 ellipsis 失效?

    因为Tabs渲染TabPane时,默认是全部渲染display: none。此时这些组件无法获取到正确的宽度或高度值。建议1.x的版本开启lazyRender,或者关闭keepDOM。0.x的版本需要使用tabList的写法。

  • 为什么在Tabs中使用Collapse/Collapsible/Resizable Table等组件的高度或宽度值不对?

    原因同上,另外如果 collapse 不需要动画,也可以通过设置 motion={false} 来关闭动画效果。此时无需获取组件的高度。