分頁標籤

輕鬆建立可協助取用、完全自訂的分頁標籤介面,並具有強健的焦點管理和鍵盤導覽支援。

安裝 Headless UI 前,請先透過 npm 安裝

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>
  )
}

Headless UI 記錄元件的許多狀態,例如目前選取的標籤、彈出視窗開啟或關閉,或者哪個選單項目目前透過鍵盤獲得焦點。

但是,由於元件預設為不帶樣式且完全無樣式,因此在你為每個狀態提供所需的樣式之前,無法在使用者介面上看到這些資訊。

用來設定 Headless 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> ) }

預設情況下,當使用者使用箭頭鍵導覽標籤頁時,系統會自動選取標籤頁。

如果您不希望在使用者按下 `Enter` 或 `Space` 鍵之前變更目前的標籤頁,請在 `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 函式渲染 div,而 Tab 函式渲染 button

使用 as 屬性函式渲染元件為不同的元素或您自己的自訂元件,確保您的自訂元件 轉送 refs,才能讓 Headless 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 元件。

命令說明

ArrowLeftArrowRight

選取上一個/下一個未停用的標籤頁。

ArrowUpArrowDown若已設定 vertical

選取上一個/下一個未停用的標籤頁。

HomePageUp

選取第一個未停用的標籤頁。

EndPageDown

選取最後一個未停用的標籤頁。

EnterSpace若已設定 manual

啟用已選的標籤頁。

主要 TabGroup 元件。

屬性預設值說明
asdiv
字串 | 元件

元素或元件分頁群組應該要呈現為這樣。

預設索引值0
數字

預設選取的索引值

選取索引值
數字

選取的索引值,如果你想要使用分頁元件當受控元件的話。

變更
(索引值:數值) => 虛空

只要選取的分頁有變動就會被呼叫的函式。

垂直錯誤
布林值

當為「真」時,TabList 的方向會變為 vertical,否則會是 horizontal

手動錯誤
布林值

當為「真」時,使用者只能透過鍵盤顯示面板,透過箭頭鍵來瀏覽後按「Enter」或「Space」鍵。預設情況下,當透過箭頭鍵瀏覽時,面板會自動顯示。請注意,此屬性不會影響滑鼠行為。

資料屬性渲染屬性說明
選取索引值

數字

目前選取的索引值。

屬性預設值說明
asdiv
字串 | 元件

元素或元件分頁清單應該要呈現為這樣。

資料屬性渲染屬性說明
選取索引值

數字

目前選取的索引值。

屬性預設值說明
as片段
字串 | 元件

元素或元件分頁應該要呈現為這樣。

禁用錯誤
布林值

是否分頁禁用.

自動對焦錯誤
布林值

是否分頁首次渲染時應該接收焦點。

資料屬性渲染屬性說明
資料-已選擇已選擇

布林值

是否分頁被選取。

資料-焦點焦點

布林值

是否分頁獲得焦點。

資料-懸停懸停

布林值

是否分頁懸停。

資料-活躍活躍

布林值

是否分頁處於活躍或按下狀態。

資料-自動對焦自動對焦

布林值

autoFocus 屬性是否設定為「真」。

屬性預設值說明
asdiv
字串 | 元件

元素或元件分頁面板應該要呈現為這樣。

資料屬性渲染屬性說明
選取索引值

數字

目前選取的索引值。

屬性預設值說明
asdiv
字串 | 元件

元素或元件分頁面板應該要呈現為這樣。

靜態錯誤
布林值

元素是否應該略過內部管理的開啟/關閉狀態。

卸載true
布林值

元素是否應該根據開啟/關閉狀態被卸載或隱藏。

資料屬性渲染屬性說明
資料-已選擇已選擇

布林值

是否分頁面板被選取。

如果您有興趣預先設計 Tailwind CSS的分頁元件範例,請查看Tailwind UI -由我們建立的一系列設計精美且製作精良的元件。

這是支持我們從事像這個一樣的開放原始碼專案的好方法,讓我們有可能改善它們並持續維護。