选项卡

轻松创建可访问的、完全可定制的选项卡界面,并提供强大的焦点管理和键盘导航支持。

要开始使用,请通过 npm 安装无头 UI

npm install @headlessui/react

选项卡使用 TabGroupTabListTabTabPanelsTabPanel 组件构建。默认情况下,第一个选项卡被选中,单击任何选项卡或使用键盘选择它将激活相应的面板。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
    <TabGroup>
      <TabList>
        <Tab>Tab 1</Tab>
        <Tab>Tab 2</Tab>
        <Tab>Tab 3</Tab>
      </TabList>
      <TabPanels>
        <TabPanel>Content 1</TabPanel>
        <TabPanel>Content 2</TabPanel>
        <TabPanel>Content 3</TabPanel>
      </TabPanels>
    </TabGroup>
  )
}

无头 UI 会跟踪每个组件的大量状态,例如当前选中的选项卡、弹出框是打开还是关闭,或者菜单中哪个项目当前通过键盘获得焦点。

但由于这些组件是无头的,并且在开箱即用时完全没有样式,因此您无法在 UI 中看到这些信息,直到您自己为每个状态提供所需的样式。

为无头 UI 组件的不同状态设置样式最简单的方法是使用每个组件公开的 data-* 属性。

例如,Tab 组件公开了一个 data-selected 属性,它告诉你该选项卡当前是否被选中,以及一个 data-hover 属性,它告诉你该选项卡当前是否被鼠标悬停。

<!-- Rendered `TabGroup` -->
<div>
  <div>
    <button>Tab 1</button>
    <button data-selected>Tab 2</button>
    <button data-hover>Tab 3</button>
  </div>
  <div>
    <div>Content 1</div>
    <div data-selected>Content 2</div>
    <div>Content 3</div>
  </div>
</div>

使用 CSS 属性选择器 根据这些数据属性的存在条件地应用样式。如果您使用的是 Tailwind CSS,数据属性修饰符 使此操作变得容易

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
    <TabGroup>
      <TabList>
<Tab className="data-[selected]:bg-blue-500 data-[selected]:text-white data-[hover]:underline">Tab 1</Tab>
<Tab className="data-[selected]:bg-blue-500 data-[selected]:text-white data-[hover]:underline">Tab 2</Tab>
<Tab className="data-[selected]:bg-blue-500 data-[selected]:text-white data-[hover]:underline">Tab 3</Tab>
</TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

请参阅 组件 API 以获取所有可用数据属性的列表。

每个组件还通过 渲染道具 公开有关其当前状态的信息,您可以使用这些信息来条件地应用不同的样式或呈现不同的内容。

例如,Tab 组件公开了一个 selected 状态,它告诉你该选项卡当前是否被选中,以及一个 hover 状态,它告诉你该选项卡当前是否被鼠标悬停。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import clsx from 'clsx'
import { Fragment } from 'react'

function Example() {
  return (
    <TabGroup>
      <TabList>
        <Tab as={Fragment}>
{({ hover, selected }) => (
<button className={clsx(hover && 'underline', selected && 'bg-blue-500 text-white')}>Tab 1</button>
)}
</Tab> <Tab as={Fragment}>
{({ hover, selected }) => (
<button className={clsx(hover && 'underline', selected && 'bg-blue-500 text-white')}>Tab 2</button>
)}
</Tab> <Tab as={Fragment}>
{({ hover, selected }) => (
<button className={clsx(hover && 'underline', selected && 'bg-blue-500 text-white')}>Tab 3</button>
)}
</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

请参阅 组件 API 以获取所有可用渲染道具的列表。

使用 disabled 道具禁用 Tab 并阻止其被选中

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
    <TabGroup>
      <TabList>
        <Tab>Tab 1</Tab>
<Tab disabled className="disabled:opacity-50">
Tab 2 </Tab> <Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

如果您已将 TabList 设置为垂直显示,请使用 vertical 道具启用使用向上和向下箭头键而不是向左和向右导航,并更新 aria-orientation 属性以供辅助技术使用。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
<TabGroup vertical>
<TabList className="flex flex-col"> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

默认情况下,选项卡会随着用户使用箭头键在选项卡之间导航而自动选中。

如果您希望在用户按下 EnterSpace 之前不更改当前选项卡,请在 TabGroup 组件上使用 manual 道具。如果选择选项卡会执行昂贵的操作并且您不想不必要地运行它,这会很有帮助。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
<TabGroup manual>
<TabList> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

manual 道具不会影响鼠标交互 - 选项卡仍会在被点击后立即选中。

要更改默认情况下选中的选项卡,请在 TabGroup 组件上使用 defaultIndex={number} 道具。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
<TabGroup defaultIndex={1}>
<TabList> <Tab>Tab 1</Tab>
{/* Selects this tab by default */}
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel>
{/* Displays this panel by default */}
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

如果您恰好提供了一个超出范围的索引,那么在初始渲染时将选中最后一个未禁用的选项卡。(例如,在上面的示例中,<TabGroup defaultIndex={5}> 将呈现第三个面板为选中状态。)

要每当选中的选项卡发生变化时运行一个函数,请在 TabGroup 组件上使用 onChange 道具。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'

function Example() {
  return (
    <TabGroup
onChange={(index) => {
console.log('Changed selected tab to:', index)
}}
>
<TabList> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

默认情况下,选项卡组件在内部管理选中的选项卡。但是,您可以使用 selectedIndex 道具和 onChange 回调自己控制选中的选项卡

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { useState } from 'react'

function Example() {
const [selectedIndex, setSelectedIndex] = useState(0)
return (
<TabGroup selectedIndex={selectedIndex} onChange={setSelectedIndex}>
<TabList> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

默认情况下,TabGroup 及其子组件分别渲染一个对该组件来说合理的默认元素。

例如,TabGroup 渲染一个 divTabList 渲染一个 divTab 渲染一个 button

使用 as 道具将组件渲染为不同的元素或您自己的自定义组件,确保您的自定义组件 转发 ref,以便无头 UI 可以正确地连接它们。

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { forwardRef } from 'react'

let MyCustomButton = forwardRef(function (props, ref) {
return <button className="..." ref={ref} {...props} />
})
function Example() { return ( <TabGroup>
<TabList as="aside">
<Tab as={MyCustomButton}>Tab 1</Tab> <Tab as={MyCustomButton}>Tab 2</Tab> <Tab as={MyCustomButton}>Tab 3</Tab> </TabList>
<TabPanels as="section">
<TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

要指示元素直接渲染其子元素,而无需包装元素,请使用 Fragment

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { Fragment } from 'react'

function Example() {
  return (
<TabGroup as={Fragment}>
<TabList> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </TabList> <TabPanels> <TabPanel>Content 1</TabPanel> <TabPanel>Content 2</TabPanel> <TabPanel>Content 3</TabPanel> </TabPanels> </TabGroup> ) }

Tab 组件获得焦点时,所有交互都会应用。

命令描述

向左箭头向右箭头

选择上一个/下一个未禁用的选项卡。

向上箭头向下箭头vertical 设置时

选择上一个/下一个未禁用的选项卡。

HomePageUp

选择第一个未禁用的选项卡。

EndPageDown

选择最后一个未禁用的选项卡。

EnterSpacemanual 设置时

激活选中的标签。

主要的 TabGroup 组件。

属性默认值描述
asdiv
String | 组件

要渲染为的元素或组件标签组应渲染为。

defaultIndex0
Number

默认选中的索引

selectedIndex
number

如果你想将标签组件用作受控组件,则为选中的索引。

onChange
(index: number) => void

每当选中的标签发生改变时就会调用此函数。

verticalfalse
Boolean

如果为真,则 TabList 的方向将为 vertical,否则将为 horizontal

manualfalse
Boolean

如果为真,则用户只能通过键盘首先使用箭头键导航到面板,然后按 EnterSpace 来显示面板。默认情况下,面板在使用箭头键导航到时会自动显示。请注意,此属性对鼠标行为没有影响。

数据属性渲染道具描述
selectedIndex

Number

当前选中的索引。

属性默认值描述
asdiv
String | 组件

要渲染为的元素或组件标签列表应渲染为。

数据属性渲染道具描述
selectedIndex

Number

当前选中的索引。

属性默认值描述
as片段
String | 组件

要渲染为的元素或组件标签应渲染为。

disabledfalse
Boolean

是否标签已禁用.

autoFocusfalse
Boolean

是否标签首次渲染时应接收焦点。

数据属性渲染道具描述
data-selectedselected

Boolean

是否标签已选中。

data-focusfocus

Boolean

是否标签已获得焦点。

data-hoverhover

Boolean

是否标签被悬停。

data-activeactive

Boolean

是否标签处于活动状态或按下状态。

data-autofocusautofocus

Boolean

autoFocus 属性是否设置为 true

属性默认值描述
asdiv
String | 组件

要渲染为的元素或组件标签面板应渲染为。

数据属性渲染道具描述
selectedIndex

Number

当前选中的索引。

属性默认值描述
asdiv
String | 组件

要渲染为的元素或组件标签面板应渲染为。

staticfalse
Boolean

元素是否应忽略内部管理的打开/关闭状态。

unmounttrue
Boolean

元素是否应根据打开/关闭状态卸载或隐藏。

数据属性渲染道具描述
data-selectedselected

Boolean

是否标签面板已选中。

如果你对预先设计好的 Tailwind CSS 标签组件示例 感兴趣,请查看 Tailwind UI——一个由我们精心设计和制作的精美组件集合。

这是一种支持我们对诸如此类的开源项目的贡献的好方法,它使我们能够改进这些项目并保持其良好的维护。