公开

一个简单、可访问的构建自定义 UI 的基础,用于显示和隐藏内容,例如可切换的手风琴面板。

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

npm install @headlessui/react

公开使用 `Disclosure`、`DisclosureButton` 和 `DisclosurePanel` 组件构建。

按钮将在点击时自动打开/关闭面板,所有组件将接收相应的 `aria-*` 相关属性,例如 `aria-expanded` 和 `aria-controls`。

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'

function Example() {
  return (
    <Disclosure>
      <DisclosureButton className="py-2">Is team pricing available?</DisclosureButton>
      <DisclosurePanel className="text-gray-500">
        Yes! You can purchase a license that you can share with your entire team.
      </DisclosurePanel>
    </Disclosure>
  )
}

无头 UI 会跟踪每个组件的很多状态,例如当前选择了哪个列表框选项,弹出框是打开还是关闭,或者在公开中哪个项目当前通过键盘获得焦点。

但由于组件是无头且开箱即用完全无样式的,因此您无法在 UI 中看到此信息,除非您自己提供想要为每个状态提供的样式。

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

例如,`DisclosureButton` 组件公开了 `data-open` 属性,它告诉您公开当前是否打开。

<!-- Rendered `Disclosure` -->
<button data-open>Do you offer technical support?</button>
<div data-open>No</div>

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

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'

function Example() {
  return (
    <Disclosure>
      <DisclosureButton className="group flex items-center gap-2">
        Do you offer technical support?
<ChevronDownIcon className="w-5 group-data-[open]:rotate-180" />
</DisclosureButton> <DisclosurePanel>No</DisclosurePanel> </Disclosure> ) }

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

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

例如,`DisclosureButton` 组件公开了 `open` 状态,它告诉您公开当前是否打开。

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import clsx from 'clsx'

function Example() {
  return (
    <Disclosure>
{({ open }) => (
<> <DisclosureButton className="flex items-center gap-2"> Do you offer technical support?
<ChevronDownIcon className={clsx('w-5', open && 'rotate-180')} />
</DisclosureButton> <DisclosurePanel>No</DisclosurePanel> </>
)}
</Disclosure> ) }

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

要动画公开面板的打开和关闭,请将 `transition` 道具添加到 `DisclosurePanel` 组件,然后使用 CSS 对过渡的不同阶段进行样式设置

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'

function Example() {
  return (
    <Disclosure as="div" className="w-full max-w-md">
      <DisclosureButton className="w-full border-b pb-2 text-left">Is team pricing available?</DisclosureButton>
      <div className="overflow-hidden py-2">
        <DisclosurePanel
transition
className="origin-top transition duration-200 ease-out data-[closed]:-translate-y-6 data-[closed]:opacity-0"
>
Yes! You can purchase a license that you can share with your entire team. </DisclosurePanel> </div> </Disclosure> ) }

`transition` 道具在内部以与 `Transition` 组件完全相同的方式实现。请参阅 过渡文档 以了解更多信息。

无头 UI 还与 React 生态系统中的其他动画库(如 Framer MotionReact Spring)很好地组合。您只需要将一些状态暴露给这些库。

例如,要使用 Framer Motion 对菜单进行动画处理,请将 `static` 道具添加到 `DisclosurePanel` 组件,然后根据 `open` 渲染道具有条件地渲染它

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'
import { AnimatePresence, easeOut, motion } from 'framer-motion'
import { Fragment } from 'react'

function Example() {
  return (
<Disclosure as="div" className="w-full max-w-md">
{({ open }) => ( <> <DisclosureButton className="w-full border-b pb-2 text-left">Is team pricing available?</DisclosureButton> <div className="overflow-hidden py-2"> <AnimatePresence> {open && (
<DisclosurePanel static as={Fragment}>
<motion.div initial={{ opacity: 0, y: -24 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -24 }} transition={{ duration: 0.2, ease: easeOut }} className="origin-top" > Yes! You can purchase a license that you can share with your entire team. </motion.div> </DisclosurePanel> )} </AnimatePresence> </div>
</>
)} </Disclosure> ) }

要手动关闭面板子级单击时的公开,请将该子级渲染为 `CloseButton`。您可以使用 `as` 道具自定义正在渲染的元素。

import { CloseButton, Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'
import MyLink from './MyLink'

function Example() {
  return (
    <Disclosure>
      <DisclosureButton>Open mobile menu</DisclosureButton>
      <DisclosurePanel>
<CloseButton as={MyLink} href="/home">
Home
</CloseButton>
</DisclosurePanel> </Disclosure> ) }

这在将公开用于移动菜单(其中包含链接,您希望在导航到下一页时公开关闭)时特别有用。

`Disclosure` 和 `DisclosurePanel` 还公开了 `close` 渲染道具,您可以使用它强制关闭面板,例如在运行异步操作后

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'

function Example() {
  return (
    <Disclosure>
      <DisclosureButton>Terms</DisclosureButton>
      <DisclosurePanel>
{({ close }) => (
<button
onClick={async () => {
await fetch('/accept-terms', { method: 'POST' })
close()
}}
>
Read and accept
</button>
)}
</DisclosurePanel> </Disclosure> ) }

默认情况下,`DisclosureButton` 会在调用 `close` 后接收焦点,但您可以通过将 ref 传递到 `close(ref)` 中来更改它。

最后,无头 UI 还提供了一个 `useClose` 钩子,当您无法轻松访问 `close` 渲染道具(例如在嵌套组件中)时,可以使用它来强制关闭最近的公开祖先

import { Disclosure, DisclosureButton, DisclosurePanel, useClose } from '@headlessui/react'

function MySearchForm() {
let close = useClose()
return ( <form onSubmit={(event) => { event.preventDefault() /* Perform search... */
close()
}}
>
<input type="search" /> <button type="submit">Submit</button> </form> ) } function Example() { return ( <Disclosure> <DisclosureButton>Filters</DisclosureButton> <DisclosurePanel> <MySearchForm /> {/* ... */} </DisclosurePanel> </Disclosure> ) }

`useClose` 钩子必须在嵌套在 `Disclosure` 中的组件中使用,否则它将无法正常工作。

`Disclosure` 及其子组件分别渲染一个适合该组件的默认元素:`Button` 渲染一个 `<button>`,`Panel` 渲染一个 `<div>`。相反,根 `Disclosure` 组件不渲染元素,而是默认直接渲染其子级。

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

import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'
import { forwardRef } from 'react'

let MyCustomButton = forwardRef(function (props, ref) {
return <button className="..." ref={ref} {...props} />
})
function Example() { return (
<Disclosure as="div">
<DisclosureButton as={MyCustomButton}>What languages do you support?</DisclosureButton>
<DisclosurePanel as="ul">
<li>HTML</li> <li>CSS</li> <li>JavaScript</li> </DisclosurePanel> </Disclosure> ) }

命令描述

EnterSpace当 `DisclosureButton` 获得焦点时。

切换面板

主要的公开组件。

道具默认值描述
asFragment
String | Component

公开应渲染为的元素或组件。公开应该渲染为。

defaultOpenfalse
Boolean

`Disclosure` 组件默认情况下是否应该打开。

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

Boolean

公开是否打开。

close

(ref) => void

关闭公开并重新聚焦 `DisclosureButton`。可以选择传递一个refHTMLElement来改为聚焦该元素。

触发组件,用于切换公开。

道具默认值描述
asbutton
String | Component

公开应渲染为的元素或组件。公开按钮应该渲染为。

autoFocusfalse
Boolean

是否公开按钮在首次渲染时应接收焦点。

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

Boolean

是否公开是打开的。

data-focusfocus

Boolean

是否公开按钮获得焦点。

data-hoverhover

Boolean

是否公开按钮悬停。

data-activeactive

Boolean

是否公开按钮处于活动或按下状态。

data-autofocusautofocus

Boolean

`autoFocus` 道具是否设置为 `true`。

此组件包含您的公开内容。

道具默认值描述
asdiv
String | Component

公开应渲染为的元素或组件。公开面板应该渲染为。

transitionfalse
Boolean

元素是否应渲染过渡属性,例如 `data-closed`、 `data-enter` 和 `data-leave`。

staticfalse
Boolean

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

unmounttrue
Boolean

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

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

Boolean

是否公开是打开的。

close

(ref) => void

关闭公开并重新聚焦 `DisclosureButton`。可以选择传递一个refHTMLElement来改为聚焦该元素。

单击此按钮将关闭最近的DisclosurePanel祖先。或者,使用useClose钩子以命令方式关闭公开面板。

道具默认值描述
asbutton
String | Component

公开应渲染为的元素或组件。关闭按钮应该渲染为。

如果您有兴趣使用 Headless UI 预先设计的Tailwind CSS 公开组件示例, 请查看Tailwind UI - 我们精心设计的精美组件集合。

这是支持我们对像这样的开源项目工作的绝佳方式,并使我们能够改进它们并保持良好的维护。