分頁標籤
輕鬆建立可協助取用、完全自訂的分頁標籤介面,並具有強健的焦點管理和鍵盤導覽支援。
安裝 Headless UI 前,請先透過 npm 安裝
npm install @headlessui/react
標籤組利用 TabGroup
、TabList
、Tab
、TabPanels
、TabPanel
組件建構而成。預設會選取第一個標籤,點選任意標籤或透過鍵盤選取,都會啟動相對應的面板。
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
函式渲染 div
、TabList
函式渲染 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
元件。
命令 | 說明 |
ArrowLeft 和 ArrowRight | 選取上一個/下一個未停用的標籤頁。 |
ArrowUp 和 ArrowDown若已設定 | 選取上一個/下一個未停用的標籤頁。 |
Home 或 PageUp | 選取第一個未停用的標籤頁。 |
End 或 PageDown | 選取最後一個未停用的標籤頁。 |
Enter 或 Space若已設定 | 啟用已選的標籤頁。 |
屬性 | 預設值 | 說明 |
as | div | 字串 | 元件 元素或元件分頁群組應該要呈現為這樣。 |
預設索引值 | 0 | 數字 預設選取的索引值 |
選取索引值 | — | 數字 選取的索引值,如果你想要使用分頁元件當受控元件的話。 |
變更 | — | (索引值:數值) => 虛空 只要選取的分頁有變動就會被呼叫的函式。 |
垂直 | 錯誤 | 布林值 當為「真」時, |
手動 | 錯誤 | 布林值 當為「真」時,使用者只能透過鍵盤顯示面板,透過箭頭鍵來瀏覽後按「Enter」或「Space」鍵。預設情況下,當透過箭頭鍵瀏覽時,面板會自動顯示。請注意,此屬性不會影響滑鼠行為。 |
資料屬性 | 渲染屬性 | 說明 |
— | 選取索引值 |
目前選取的索引值。 |
屬性 | 預設值 | 說明 |
as | div | 字串 | 元件 元素或元件分頁清單應該要呈現為這樣。 |
資料屬性 | 渲染屬性 | 說明 |
— | 選取索引值 |
目前選取的索引值。 |
屬性 | 預設值 | 說明 |
as | 片段 | 字串 | 元件 元素或元件分頁應該要呈現為這樣。 |
禁用 | 錯誤 | 布林值 是否分頁禁用. |
自動對焦 | 錯誤 | 布林值 是否分頁首次渲染時應該接收焦點。 |
資料屬性 | 渲染屬性 | 說明 |
資料-已選擇 | 已選擇 |
是否分頁被選取。 |
資料-焦點 | 焦點 |
是否分頁獲得焦點。 |
資料-懸停 | 懸停 |
是否分頁懸停。 |
資料-活躍 | 活躍 |
是否分頁處於活躍或按下狀態。 |
資料-自動對焦 | 自動對焦 |
|
屬性 | 預設值 | 說明 |
as | div | 字串 | 元件 元素或元件分頁面板應該要呈現為這樣。 |
資料屬性 | 渲染屬性 | 說明 |
— | 選取索引值 |
目前選取的索引值。 |
屬性 | 預設值 | 說明 |
as | div | 字串 | 元件 元素或元件分頁面板應該要呈現為這樣。 |
靜態 | 錯誤 | 布林值 元素是否應該略過內部管理的開啟/關閉狀態。 |
卸載 | true | 布林值 元素是否應該根據開啟/關閉狀態被卸載或隱藏。 |
資料屬性 | 渲染屬性 | 說明 |
資料-已選擇 | 已選擇 |
是否分頁面板被選取。 |
如果您有興趣預先設計 Tailwind CSS的分頁元件範例,請查看Tailwind UI -由我們建立的一系列設計精美且製作精良的元件。
這是支持我們從事像這個一樣的開放原始碼專案的好方法,讓我們有可能改善它們並持續維護。