开关 (切换)
开关是用于在两种状态之间切换值的良好界面,并提供与原生复选框元素相同的语义和键盘导航。
要开始,请通过 npm 安装无状态 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> ) }
无状态 UI 会跟踪每个组件的大量状态,例如当前选择了哪个开关选项,弹出窗口是打开还是关闭,或者菜单中哪个项目当前通过键盘处于活动状态。
但由于组件是无状态的,并且在开箱即用时完全没有样式,所以您无法看到此信息,直到您自己提供每个状态所需的样式。
每个组件都通过 渲染道具 公开其当前状态的信息,您可以使用这些信息来有条件地应用不同的样式或渲染不同的内容。
例如,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> ) }
有关每个组件的完整渲染道具 API,请参阅 组件 API 文档。
每个组件还通过 data-headlessui-state
属性公开其当前状态的信息,您可以使用这些信息来有条件地应用不同的样式。
当 渲染道具 API 中的任何状态为 true
时,它们将作为以空格分隔的字符串列出,因此您可以使用 CSS 属性选择器 以 [attr~=value]
的形式对其进行定位。
例如,以下是 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> ) }
默认情况下,开关会渲染一个 button
以及您传递给它的任何子元素。这会使实现某些 UI 变得更加困难,因为子元素将嵌套在按钮中。
在这些情况下,您可以使用 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
将切换开关,就像原生 HTML 复选框中的标签一样。如果您希望标签不可点击(如果您觉得这对于您的设计没有意义,您可能需要这样做),您可以在 Switch.Label
组件中添加一个 passive
道具
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 <form>
中使用开关,并像使用原生 HTML 表单控件一样进行传统的表单提交。
默认情况下,当开关选中时,值为 'on'
,而当开关未选中时,值为 'off'
。
<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
道具而不是 checked
道具,无状态 UI 将为您内部跟踪其状态,允许您将其用作 非受控组件。
您可以通过 Switch
组件上的 checked
渲染道具访问当前状态。
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 收集其状态而不是使用 React 状态跟踪其状态的表单 API 时,这可以简化您的代码。
您提供的任何 onChange
道具都将在组件的值发生更改时被调用,以防您需要运行任何副作用,但您不需要使用它来自己跟踪组件的状态。
由于开关通常始终呈现到 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> ); }
由于它们是无状态的,无状态 UI 组件也与 React 生态系统中的其他动画库(如 Framer Motion 和 React Spring)组合良好。
默认情况下,Switch
的子元素将用作屏幕阅读器的标签。如果您使用的是 Switch.Label
,您的 Switch
组件的内容将被辅助技术忽略。
单击 Switch
或 Switch.Label
会打开和关闭开关。
命令 | 描述 |
空格 当 | 切换开关 |
回车 当在表单中时 | 提交表单 |
道具 | 默认值 | 描述 |
as | 按钮 | 字符串 | 组件
|
选中 | — | 布尔值 开关是否选中。 |
defaultChecked | — | T 用作非受控组件时的默认选中值。 |
onChange | — | (value: 布尔值) => void 切换开关时要调用的函数。 |
名称 | — | 字符串 在表单中使用此组件时使用的名称。 |
价值 | — | 字符串 在表单中使用此组件时,如果选中,使用的值。 |
渲染道具 | 描述 |
选中 |
开关是否选中。 |
道具 | 默认值 | 描述 |
as | 标签 | 字符串 | 组件
|
被动 | 错误的 | 布尔值 如果为 true,单击标签将不会切换 |
道具 | 默认值 | 描述 |
as | p | 字符串 | 组件
|
道具 | 默认值 | 描述 |
as | 片段 | 字符串 | 组件
|