浮动窗口
特性
- 可受控或不受控。
- 可自定义侧边、对齐方式、偏移量、碰撞处理。
- 可选择渲染一个指示箭头。
- 焦点完全受管理且可自定义。
- 支持模态和非模态模式。
- 关闭和分层行为高度可自定义。
安装
从命令行安装此组件。
$ npm add reka-ui
结构
导入所有部分并将其组合。
<script setup>
import { PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui'
</script>
<template>
<PopoverRoot>
<PopoverTrigger />
<PopoverAnchor />
<PopoverPortal>
<PopoverContent>
<PopoverClose />
<PopoverArrow />
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
API 参考
根
包含浮动窗口的所有部分。
属性 | 默认 | 类型 |
---|---|---|
defaultOpen | false | 布尔值 浮动窗口初始渲染时的打开状态。当您不需要控制其打开状态时使用。 |
modal | false | 布尔值 浮动窗口的模态性。设置为 true 时,与外部元素的交互将被禁用,并且只有浮动窗口内容对屏幕阅读器可见。 |
open | 布尔值 浮动窗口的受控打开状态。 |
事件触发 | 载荷 |
---|---|
update:open | [value: boolean] 当浮动窗口的打开状态改变时调用的事件处理程序。 |
插槽(默认) | 载荷 |
---|---|
open | 布尔值 当前打开状态 |
close | (): void 关闭浮动窗口 |
触发器
切换浮动窗口的按钮。默认情况下,PopoverContent
将根据触发器进行定位。
属性 | 默认 | 类型 |
---|---|---|
as | 'button' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
数据属性 | 值 |
---|---|
[data-state] | "打开" | "关闭" |
锚点
一个可选元素,用于定位 PopoverContent
。如果未使用此部分,内容将与 PopoverTrigger
并列定位。
属性 | 默认 | 类型 |
---|---|---|
as | 'div' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
reference | ReferenceElement 用于定位的参考(或锚点)元素。 如果未提供,将使用当前组件作为锚点。 |
门户
使用时,将内容部分传送到 body
中。
属性 | 默认 | 类型 |
---|---|---|
defer | 布尔值 延迟解析 Teleport 目标,直到应用程序的其他部分挂载(需要 Vue 3.5.0+) | |
disabled | 布尔值 禁用传送并内联渲染组件 | |
forceMount | 布尔值 当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 | |
to | 字符串 | HTMLElement Vue 原生传送组件属性 |
内容
浮动窗口打开时弹出的组件。
属性 | 默认 | 类型 |
---|---|---|
align | 'start' | 'center' | 'end' 相对于触发器的首选对齐方式。当发生碰撞时可能会改变。 | |
alignOffset | 数字 与 | |
arrowPadding | 数字 箭头与内容边缘之间的内边距。如果您的内容有 border-radius,这将防止它溢出角落。 | |
as | 'div' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
avoidCollisions | 布尔值 当为 | |
collisionBoundary | Element | (Element | null)[] | null 用作碰撞边界的元素。默认情况下是视口,但您可以提供其他元素以包含在此检查中。 | |
collisionPadding | 数字 | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>> 碰撞检测应发生的距边界边缘的像素距离。接受一个数字(所有边相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。 | |
disableOutsidePointerEvents | 布尔值 当为 | |
disableUpdateOnLayoutShift | 布尔值 当布局发生偏移时,是否禁用更新内容位置。 | |
forceMount | 布尔值 当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 | |
hideWhenDetached | 布尔值 当触发器完全被遮挡时,是否隐藏内容。 | |
positionStrategy | 'fixed' | 'absolute' 要使用的 CSS position 属性类型。 | |
prioritizePosition | 布尔值 强制内容在视口内定位。 可能与参考元素重叠,这可能不是期望的结果。 | |
reference | ReferenceElement 将设置为浮动元素定位参考的自定义元素或虚拟元素。 如果提供,它将替换默认的锚点元素。 | |
side | 'top' | 'right' | 'bottom' | 'left' 打开时,相对于触发器渲染的首选侧边。当发生碰撞且启用 avoidCollisions 时,将反转。 | |
sideOffset | 数字 与触发器的像素距离。 | |
sticky | '部分' | '始终' 对齐轴上的粘性行为。 | |
updatePositionStrategy | '始终' | '优化' 在每个动画帧更新浮动元素位置的策略。 |
事件触发 | 载荷 |
---|---|
closeAutoFocus | [事件: Event] 关闭时自动聚焦调用的事件处理程序。可阻止其默认行为。 |
escapeKeyDown | [事件: KeyboardEvent] 按下 Esc 键时调用的事件处理程序。可阻止其默认行为。 |
focusOutside | [事件: FocusOutsideEvent] 当焦点移出 |
interactOutside | [事件: PointerDownOutsideEvent | FocusOutsideEvent] 当 |
openAutoFocus | [事件: Event] 打开时自动聚焦调用的事件处理程序。可阻止其默认行为。 |
pointerDownOutside | [事件: PointerDownOutsideEvent] 当 |
数据属性 | 值 |
---|---|
[data-state] | "打开" | "关闭" |
[data-side] | "左" | "右" | "下" | "上" |
[data-align] | "开始" | "结束" | "居中" |
CSS 变量 | 描述 |
---|---|
--reka-popover-content-transform-origin | 从内容和箭头位置/偏移量计算的 transform-origin |
--reka-popover-content-available-width | 触发器与边界边缘之间的剩余宽度 |
--reka-popover-content-available-height | 触发器与边界边缘之间的剩余高度 |
--reka-popover-trigger-width | 触发器的宽度 |
--reka-popover-trigger-height | 触发器的高度 |
箭头
一个可选的箭头元素,与浮动窗口一起渲染。这可用于帮助在视觉上将锚点与 PopoverContent
连接起来。必须在 PopoverContent
内部渲染。
属性 | 默认 | 类型 |
---|---|---|
as | 'svg' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
高度 | 5 | 数字 箭头的像素高度。 |
rounded | 布尔值 当为 | |
宽度 | 10 | 数字 箭头的像素宽度。 |
关闭
关闭已打开浮动窗口的按钮。
属性 | 默认 | 类型 |
---|---|---|
as | 'button' | AsTag | Component 此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值 更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
示例
限制内容大小
您可能希望限制内容的宽度以匹配触发器的宽度。您可能还希望限制其高度,使其不超过视口。
我们公开了一些 CSS 自定义属性,例如 --reka-popover-trigger-width
和 --reka-popover-content-available-height
以支持此功能。使用它们来限制内容尺寸。
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui'
</script>
<template>
<PopoverRoot>
<PopoverTrigger>…</PopoverTrigger>
<PopoverPortal>
<PopoverContent
class="PopoverContent"
:side-offset="5"
>
…
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.PopoverContent {
width: var(--reka-popover-trigger-width);
max-height: var(--reka-popover-content-available-height);
}
原点感知动画
我们公开了一个 CSS 自定义属性 --reka-popover-content-transform-origin
。使用它来根据 side
、sideOffset
、align
、alignOffset
和任何碰撞从其计算出的原点对内容进行动画处理。
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui'
</script>
<template>
<PopoverRoot>
<PopoverTrigger>…</PopoverTrigger>
<PopoverPortal>
<PopoverContent class="PopoverContent">
…
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.PopoverContent {
transform-origin: var(--reka-popover-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
碰撞感知动画
我们公开了 data-side
和 data-align
属性。它们的值将在运行时改变以反映碰撞。使用它们来创建碰撞和方向感知的动画。
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui'
</script>
<template>
<PopoverRoot>
<PopoverTrigger>…</PopoverTrigger>
<PopoverPortal>
<PopoverContent class="PopoverContent">
…
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.PopoverContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.PopoverContent[data-side="top"] {
animation-name: slideUp;
}
.PopoverContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
带自定义锚点
如果您不想使用触发器作为锚点,可以将内容锚定到另一个元素。
<script setup>
import { PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui'
</script>
<template>
<PopoverRoot>
<PopoverAnchor as-child>
<div class="Row">
Row as anchor <PopoverTrigger>Trigger</PopoverTrigger>
</div>
</PopoverAnchor>
<PopoverPortal>
<PopoverContent>…</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.Row {
background-color: gainsboro;
padding: 20px;
}
使用插槽属性关闭
或者,您可以使用 PopoverRoot
插槽属性提供的 close
方法以编程方式关闭浮动窗口。
<script setup>
import { PopoverAnchor, PopoverArrow, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui'
</script>
<template>
<PopoverRoot v-slot="{ close }">
<PopoverTrigger>Open</PopoverTrigger>
<PopoverAnchor />
<PopoverPortal>
<PopoverContent>
<button type="submit" @click="close">
Submit
</button>
<PopoverArrow />
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
无障碍性
键盘交互
键 | 描述 |
---|---|
空格键 | 打开/关闭浮动窗口。 |
回车键 | 打开/关闭浮动窗口。 |
Tab | 将焦点移至下一个可聚焦元素 |
Shift + Tab | 将焦点移至上一个可聚焦元素 |
Esc键 | 关闭浮动窗口并将焦点移至 PopoverTrigger 。 |
自定义 API
通过将原始部件抽象到您自己的组件中来创建您自己的 API。
抽象化箭头并设置默认配置
此示例抽象了 PopoverArrow
部分并设置了默认的 sideOffset
配置。
用法
<script setup lang="ts">
import { Popover, PopoverContent, PopoverTrigger } from './your-popover'
</script>
<template>
<Popover>
<PopoverTrigger>Popover trigger</PopoverTrigger>
<PopoverContent>Popover content</PopoverContent>
</Popover>
</template>
实现
// your-popover.ts
export { default as PopoverContent } from 'PopoverContent.vue'
export { PopoverRoot as Popover, PopoverTrigger } from 'reka-ui'
<!-- PopoverContent.vue -->
<script setup lang="ts">
import type { PopoverContentEmits, PopoverContentProps } from 'reka-ui'
import { PopoverContent, PopoverPortal, useForwardPropsEmits } from 'reka-ui'
const props = defineProps<PopoverContentProps>()
const emits = defineEmits<PopoverContentEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<PopoverPortal>
<PopoverContent v-bind="{ ...forwarded, ...$attrs }">
<slot />
</PopoverContent>
</PopoverPortal>
</template>