Popover
弹出框非常适合浮动面板,包含任意内容,例如导航菜单、移动菜单和飞出菜单。
要开始,请通过 npm 安装无头 UI。
请注意,**此库仅支持 Vue 3**。
npm install @headlessui/vue
弹出框使用 Popover
、PopoverButton
和 PopoverPanel
组件构建。
单击 PopoverButton
会自动打开/关闭 PopoverPanel
。当面板打开时,单击面板内容以外的任何位置、按 Esc 键或从面板中 Tab 键退出都会关闭弹出框。
<template> <Popover class="relative"> <PopoverButton>Solutions</PopoverButton> <PopoverPanel class="absolute z-10"> <div class="grid grid-cols-2"> <a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </div> <img src="/solutions.jpg" alt="" /> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>
这些组件完全没有样式,因此如何样式化 Popover
由您决定。在我们的示例中,我们对 PopoverPanel
使用绝对定位,以将其定位在 PopoverButton
附近,并且不会干扰正常的文档流。
无头 UI 会跟踪有关每个组件的大量状态,例如当前选中的列表框选项、弹出框是打开还是关闭,或者当前通过键盘激活的弹出框中的项目。
但是,由于组件是无头的,并且在开箱即用时完全没有样式,因此您无法看到此信息在您的 UI 中,直到您自己提供每个状态所需的样式。
每个组件都通过 插槽属性 公开有关其当前状态的信息,您可以使用这些属性有条件地应用不同的样式或渲染不同的内容。
例如,Popover
组件公开了 open
状态,该状态告诉您弹出框当前是否打开。
<template>
<Popover v-slot="{ open }"><!-- Use the `open` state to conditionally change the direction of the chevron icon. --> <PopoverButton> Solutions<ChevronDownIcon :class="{ 'rotate-180 transform': open }" /></PopoverButton> <PopoverPanel> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' import { ChevronDownIcon } from '@heroicons/vue/20/solid' </script>
有关所有可用插槽属性的完整列表,请参阅 组件 API 文档。
每个组件还通过 data-headlessui-state
属性公开有关其当前状态的信息,您可以使用该属性有条件地应用不同的样式。
当 插槽属性 API 中的任何状态为 true
时,它们将在此属性中列出为以空格分隔的字符串,以便您可以使用 CSS 属性选择器 以 [attr~=value]
形式进行定位。
例如,以下是弹出框打开时 Popover
组件的渲染内容
<!-- Rendered `Popover` --> <div data-headlessui-state="open"> <button data-headlessui-state="open">Solutions</button> <div data-headlessui-state="open"> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </div> </div>
如果您使用的是 Tailwind CSS,您可以使用 @headlessui/tailwindcss 插件使用修饰符(例如 ui-open:*
)定位此属性。
<template> <Popover> <PopoverButton> Solutions
<ChevronDownIcon class="ui-open:rotate-180 ui-open:transform" /></PopoverButton> <PopoverPanel> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' import { ChevronDownIcon } from '@heroicons/vue/20/solid' </script>
默认情况下,PopoverPanel
将根据 Popover
组件本身内部跟踪的打开状态自动显示/隐藏。
<template> <Popover> <PopoverButton>Solutions</PopoverButton> <!-- By default, the `PopoverPanel` will automatically show/hide when the `PopoverButton` is pressed. --> <PopoverPanel> <!-- ... --> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>
如果您希望自己处理此操作(也许是因为出于某种原因需要添加额外的包装元素),您可以将 static
属性传递给 PopoverPanel
以告诉它始终渲染,然后使用 open
插槽属性来控制何时自己显示/隐藏面板。
<template>
<Popover v-slot="{ open }"><PopoverButton>Solutions</PopoverButton> <div v-if="open"><!--Using the `static` prop, the `PopoverPanel` is alwaysrendered and the `open` state is ignored.--> <PopoverPanel static> <!-- ... --> </PopoverPanel> </div> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>
由于弹出框可以包含交互式内容(例如表单控件),因此我们不能像在 Menu
组件中那样,当您单击其中的某个内容时自动关闭弹出框。
要在单击面板的子元素时手动关闭弹出框,请将该子元素渲染为 PopoverButton
。您可以使用 :as
属性来自定义正在渲染的元素。
<template> <Popover> <PopoverButton>Solutions</PopoverButton> <PopoverPanel>
<PopoverButton :as="MyLink" href="/insights">Insights</PopoverButton><!-- ... --> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' import MyLink from './MyLink' </script>
或者,Popover
和 PopoverPanel
公开了一个 close()
插槽属性,您可以使用它来强制关闭面板,例如在运行异步操作后。
<template> <Popover> <PopoverButton>Solutions</PopoverButton>
<PopoverPanel v-slot="{ close }"><button @click="accept(close)">Read and accept</button></PopoverPanel></Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'async function accept(close) {await fetch('/accept-terms', { method: 'POST' })close()}</script>
默认情况下,PopoverButton
在调用 close()
后会获得焦点,但您可以通过将 ref 传递到 close(ref)
来更改此行为。
如果您希望在每次打开弹出框时在应用程序 UI 上方样式化一个背景,请使用 PopoverOverlay
组件。
<template> <Popover v-slot="{ open }"> <PopoverButton>Solutions</PopoverButton>
<PopoverOverlay class="fixed inset-0 bg-black opacity-30" /><PopoverPanel> <!-- ... --> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverOverlay, PopoverButton, PopoverPanel, } from '@headlessui/vue' </script>
在此示例中,我们将 PopoverOverlay
放置在 DOM 中的 Panel
之前,这样它就不会覆盖面板的内容。
但与所有其他组件一样,PopoverOverlay
是完全无头的,因此如何样式化它由您决定。
要动画化弹出框面板的打开/关闭,可以使用 Vue 的内置 <transition>
元素。您只需将 PopoverPanel
包含在 <transition>
中,过渡就会自动应用。
<template> <Popover> <PopoverButton>Solutions</PopoverButton> <!-- Use the built-in `transition` component to add transitions. -->
<transitionenter-active-class="transition duration-200 ease-out"enter-from-class="translate-y-1 opacity-0"enter-to-class="translate-y-0 opacity-100"leave-active-class="transition duration-150 ease-in"leave-from-class="translate-y-0 opacity-100"leave-to-class="translate-y-1 opacity-0"><PopoverPanel> <!-- ... --> </PopoverPanel> </transition> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>
如果您希望为弹出框的不同子元素协调多个过渡,请查看 无头 UI 中包含的过渡组件。
在渲染多个相关的弹出框时,例如在网站的标题导航中,请使用 PopoverGroup
组件。这将确保面板在用户在组内的弹出框之间进行 Tab 键操作时保持打开状态,但一旦用户在组外进行 Tab 键操作,就会关闭任何打开的面板。
<template>
<PopoverGroup><Popover> <PopoverButton>Product</PopoverButton> <PopoverPanel> <!-- ... --> </PopoverPanel> </Popover> <Popover> <PopoverButton>Solutions</PopoverButton> <PopoverPanel> <!-- ... --> </PopoverPanel> </Popover></PopoverGroup></template> <script setup> import { PopoverGroup, Popover, PopoverButton, PopoverPanel, } from '@headlessui/vue' </script>
Popover
及其子组件分别渲染一个对该组件来说明智的默认元素:Popover
、Overlay
、Panel
和 Group
组件都渲染一个 <div>
,而 Button
组件则渲染一个 <button>
。
这很容易使用 as
属性更改,该属性存在于每个组件中。
<template> <!-- Render a `nav` instead of a `div` -->
<Popover as="nav"><PopoverButton>Solutions</PopoverButton> <!-- Render a `form` instead of a `div` --><PopoverPanel as="form"><!-- ... --></PopoverPanel></Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>
在打开的面板上按 Tab 键将使面板内容中的第一个可聚焦元素获得焦点。如果正在使用 PopoverGroup
,Tab 键将从打开的面板内容的末尾循环到下一个弹出框的按钮。
单击 PopoverButton
将切换面板打开和关闭。单击打开面板以外的任何位置将关闭该面板。
命令 | 描述 |
Enter 或 Space当 | 切换面板 |
Esc | 关闭任何打开的弹出框 |
Tab | 在打开的面板内容中循环 从打开的面板中 Tab 键退出将关闭该面板,并且从一个打开的面板到同级弹出框的按钮(在 PopoverGroup 内)的 Tab 键操作将关闭第一个面板。 |
Shift + Tab | 在焦点顺序中向后循环 |
支持嵌套弹出框,并且所有面板将在根面板关闭时正确关闭。
所有相关的 ARIA 属性都会自动管理。
以下是弹出框与其他类似组件的比较方式
-
<Menu />
. 弹出框比菜单更通用。菜单只支持非常有限的内容,并且具有特定的无障碍语义。箭头键还会导航菜单的项目。菜单最适合 UI 元素,这些元素类似于在大多数操作系统标题栏中找到的菜单。如果您的浮动面板包含图像或比简单链接更多的标记,请使用弹出框。 -
<Disclosure />
. 公开用于通常会重新流文档的内容,例如手风琴。弹出框还在公开之上具有额外的行为:它们会渲染覆盖层,并且当用户单击覆盖层(通过单击弹出框内容之外)或按 Esc 键时会关闭。如果您的 UI 元素需要此行为,请使用弹出框而不是公开。 -
<Dialog />
. 对话框旨在完全吸引用户的注意力。它们通常在屏幕中央渲染一个浮动面板,并使用背景来使应用程序内容的其余部分变暗。它们还会捕获焦点,并防止在对话框被关闭之前从对话框内容中 Tab 键退出。弹出框更具上下文相关性,通常定位在触发它们的元素附近。
主弹出框组件。
属性 | 默认值 | 描述 |
as | div | 字符串 | 组件
|
插槽属性 | 描述 |
open |
弹出框是打开还是关闭。 |
close |
关闭弹出框并重新聚焦 |
属性 | 默认值 | 描述 |
as | div | 字符串 | 组件
|
插槽属性 | 描述 |
open |
弹出框是打开还是关闭。 |
这是用于切换 Popover 的触发组件。你也可以在 PopoverPanel
内使用 PopoverButton
组件,如果这样做,它将充当 关闭
按钮。我们也会确保在按钮上提供正确的 aria-*
属性。
属性 | 默认值 | 描述 |
as | 按钮 | 字符串 | 组件
|
插槽属性 | 描述 |
open |
弹出框是打开还是关闭。 |
属性 | 默认值 | 描述 |
as | div | 字符串 | 组件
|
焦点 | false | 布尔值 这将在 |
静态 | false | 布尔值 元素是否应该忽略内部管理的打开/关闭状态。 注意: |
卸载 | true | 布尔值 元素是否应该根据打开/关闭状态进行卸载或隐藏。 注意: |
插槽属性 | 描述 |
open |
弹出框是打开还是关闭。 |
close |
关闭弹出框并重新聚焦 |
通过将相关联的兄弟 Popover 包含在 PopoverGroup
中来链接它们。从一个 PopoverPanel
中跳出将使下一个 Popover 的 PopoverButton
获得焦点,而从 PopoverGroup
中跳出将完全关闭组内的所有 Popover。
属性 | 默认值 | 描述 |
as | div | 字符串 | 组件
|