開關(切換)

開關是一種讓人愉快的介面,可用於在兩種狀態之間切換一個值,並提供與原生核取方塊元素相同語意和鍵盤導覽功能。

首先,請透過 npm 安裝 Headless UI

npm install @headlessui/react

開關元件是使用 Switch 元件建立。您可以直接按一下元件切換開關,或在元件取得焦點時按下空白鍵切換。

切換開關時,會呼叫 onChange 函數並將 checked 值取負。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch checked={enabled} onChange={setEnabled} className={`${ enabled ? 'bg-blue-600' : 'bg-gray-200' } relative inline-flex h-6 w-11 items-center rounded-full`} > <span className="sr-only">Enable notifications</span> <span className={`${ enabled ? 'translate-x-6' : 'translate-x-1' } inline-block h-4 w-4 transform rounded-full bg-white transition`} /> </Switch> ) }

Headless UI 會追蹤關於各個元件的許多狀態,例如目前選取哪個開關選項、浮動選單是開啟還是關閉,或目前使用鍵盤在選單中的哪個項目是目前的焦點。

但由於元件是無形的且開箱後完全沒有設定樣式,直到您自己提供想要套用給各個狀態的樣式之前,您都無法在使用介面中看到這些資訊。

各個元件透過程用 render props 揭露其目前狀態的資訊,您可以使用它有條件地套用不同的樣式或呈現不同的內容。

例如,Switch 元件會顯示 checked 狀態,表示開關目前是否處於選取狀態。

import { useState, Fragment } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch checked={enabled} onChange={setEnabled} as={Fragment}>
{({ checked }) => (
/* Use the `checked` state to conditionally style the button. */ <button className={`${
checked ? 'bg-blue-600' : 'bg-gray-200'
}
relative inline-flex h-6 w-11 items-center rounded-full`
}
>
<span className="sr-only">Enable notifications</span> <span className={`${
checked ? 'translate-x-6' : 'translate-x-1'
}
inline-block h-4 w-4 transform rounded-full bg-white transition`
}
/>
</button> )} </Switch> ) }

想了解各個元件完整的 render prop API,請參閱 元件 API 文件

每個元件也會經由 `data-headlessui-state` 屬性顯示關於其目前的狀態資訊,你可使用該屬性有條件地套用不同的樣式。

渲染道具 API 中任何一種狀態為 `true` 時,這些狀態會在這項屬性中列為空白分隔的字串,讓你可以在 `[attr~=value]` 格式中,透過 CSS 屬性選取器 來針對它們。

例如,這裡是切換已開啟時,`Switch` 元件會渲染的內容

<!-- Rendered `Switch` --> <button data-headlessui-state="checked"></button>

如果你使用 Tailwind CSS,你可以使用 @headlessui/tailwindcss 外掛程式來以 `ui-checked:*` 等修改器來針對此屬性。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch checked={enabled} onChange={setEnabled}
className="relative inline-flex h-6 w-11 items-center rounded-full ui-checked:bg-blue-600 ui-not-checked:bg-gray-200"
>
<span className="sr-only">Enable notifications</span>
<span className="inline-block h-4 w-4 transform rounded-full bg-white transition ui-checked:translate-x-6 ui-not-checked:translate-x-1" />
</Switch> ) }

預設情況下,`Switch` 會渲染一個 `button`,以及你傳遞到其中作為子項目的任何內容。這可能會讓實作特定的使用者介面變得更困難,因為子項目會巢狀在按鈕中。

在這些情況下,你可以使用 `Switch.Label` 元件以獲得更大的彈性。

此範例示範如何使用 `Switch.Group`、`Switch` 和 `Switch.Label` 元件,將標籤渲染成按鈕的兄弟項目。請注意,`Switch.Label` 會在 `Switch` 元件旁邊使用,而他們兩者都必須渲染在父層 `Switch.Group` 元件中。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return (
<Switch.Group>
<div className="flex items-center">
<Switch.Label className="mr-4">Enable notifications</Switch.Label>
<Switch checked={enabled} onChange={setEnabled} className={`${ enabled ? 'bg-blue-600' : 'bg-gray-200' } relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2`} > <span className={`${ enabled ? 'translate-x-6' : 'translate-x-1' } inline-block h-4 w-4 transform rounded-full bg-white transition-transform`} /> </Switch> </div>
</Switch.Group>
) }

預設情況下,按一下 `Switch.Label` 會切換 Switch,就像原生 HTML 核取方塊中的標籤一樣。如果你想讓標籤無法按一下(如果你設計中這樣做不合理,你可能會想這樣做),你可以將 `passive` 道具加入到 `Switch.Label` 元件中。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch.Group>
<Switch.Label passive>Enable notifications</Switch.Label>
<Switch checked={enabled} onChange={setEnabled}> {/* ... */} </Switch> </Switch.Group> ) }

如果你將 `name` 道具加入到切換中,一個隱藏的 `input` 元素將會被渲染出來,並與切換狀態保持同步。

import { useState } from 'react' import { Switch } from '@headlessui/react' function Example() { const [enabled, setEnabled] = useState(true) return ( <form action="/notification-settings" method="post">
<Switch checked={enabled} onChange={setEnabled} name="notifications">
{/* ... */} </Switch> <button>Submit</button> </form> ) }

這讓你可以在原生 HTML `

` 中使用切換,並進行傳統的範本提交,就像你的切換是原生 HTML 範本控制項一樣。

預設情況下,當切換開啟時,值將會是 `'on'`,當切換未開啟時,值將不會出現。

<input type="hidden" name="notifications" value="on" />

你可以在需要時使用 `value` 道具來自訂值。

import { useState } from 'react' import { Switch } from '@headlessui/react' function Example() { const [enabled, setEnabled] = useState(true) return ( <form action="/accounts" method="post"> <Switch checked={enabled} onChange={setEnabled}
name="terms"
value="accept"
>
{/* ... */} </Switch> <button>Submit</button> </form> ) }

當切換開啟時,隱藏的輸入將會使用你的自訂值。

<input type="hidden" name="terms" value="accept" />

如果您在 Switch 中提供 defaultChecked prop 而不是 checked prop,Headless UI 會為您在內部追蹤其狀態,讓您能當作 非受控元件 使用。

您可以透過 Switch 元件的 checked render prop 存取目前狀態。

import { Fragment } from 'react' import { Switch } from '@headlessui/react' function Example() { return ( <form action="/accounts" method="post">
<Switch name="terms-of-service" defaultChecked={true} as={Fragment}>
{({ checked }) => ( <button className={`${ checked ? 'bg-blue-600' : 'bg-gray-200' } relative inline-flex h-6 w-11 items-center rounded-full`} > <span className="sr-only">Enable notifications</span> <span className={`${ checked ? 'translate-x-6' : 'translate-x-1' } inline-block h-4 w-4 transform rounded-full bg-white transition`} /> </button> )} </Switch> <button>Submit</button> </form> ) }

在將清單方塊 與 HTML 表單一起使用,或者使用會透過 FormData 代替追蹤元件狀態的方式來收集其狀態的表單 API 時,這可簡化您的程式碼。

在元件值變更時,您提供的任何 onChange prop 都仍會被呼叫,以防您需要執行任何副作用,但您不需要再親自用它來追蹤元件狀態。

由於切換器通常都直接呈現在 DOM 中(而不是像其他元件那樣會掛載/卸載),因此通常只要使用簡單的 CSS 過渡效果就足以讓切換器有動畫效果。

import { useState } from "react"; import { Switch } from "@headlessui/react"; function MyToggle() { const [enabled, setEnabled] = useState(false); return ( <Switch checked={enabled} onChange={setEnabled}> <span /* Transition the switch's knob on state change */
className={`transform transition ease-in-out duration-200
${enabled ? "translate-x-9" : "translate-x-0"}
`}
/> {/* ... */} </Switch> ); }

由於 Headless UI 的元件不依賴呈現,因此可以與 React 生態系統中的其他動畫函式庫很好地組成,例如 Framer MotionReact Spring

預設情況下,Switch 的子代會作為螢幕閱讀器的標籤使用。如果您使用 Switch.Label,輔助技術會忽略 Switch 元件的內容。

按一下 SwitchSwitch.Label 可切換切換器開或關。

指令描述

Switch 有焦點時按一下 空格鍵

切換切換器

在表單中有焦點時按一下 Enter 鍵

送出表單

所有相關的 ARIA 屬性都會自動管理。

主要的開關元件。

屬性預設值描述
asbutton
字串 | 元件

Switch 應呈現的元素或元件。

checked
布林值

開關是否被選取。

defaultChecked
T

使用非受控元件時,預設被選取的值。

onChange
(value: 布林值) => void

開關被切換時要呼叫的函式。

name
字串

此元件在表單中使用時,所使用的名稱。

value
字串

此元件在表單中使用時,如果被選取,所使用的值。

渲染屬性描述
checked

布林值

開關是否被選取。

屬性預設值描述
aslabel
字串 | 元件

Switch.Label 應呈現的元素或元件。

passivefalse
布林值

如果為 true,則點擊標籤不會切換 Switch

屬性預設值描述
asp
字串 | 元件

Switch.Description 應呈現的元素或元件。

屬性預設值描述
as片段
字串 | 元件

Switch.Group 應呈現的元素或元件。

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

這是支持我們參與此類開源專案的絕佳方式,並讓我們得以改進和持續維護這些專案。