Reka UI 标志Reka
backdrop
组件

浮动窗口

在一个门户中显示富文本内容,通过按钮触发。

特性

  • 可受控或不受控。
  • 可自定义侧边、对齐方式、偏移量、碰撞处理。
  • 可选择渲染一个指示箭头。
  • 焦点完全受管理且可自定义。
  • 支持模态和非模态模式。
  • 关闭和分层行为高度可自定义。

安装

从命令行安装此组件。

sh
$ npm add reka-ui

结构

导入所有部分并将其组合。

vue
<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 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

数据属性
[data-state]"打开" | "关闭"

锚点

一个可选元素,用于定位 PopoverContent。如果未使用此部分,内容将与 PopoverTrigger 并列定位。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

reference
ReferenceElement

用于定位的参考(或锚点)元素。

如果未提供,将使用当前组件作为锚点。

门户

使用时,将内容部分传送到 body 中。

属性默认类型
defer
布尔值

延迟解析 Teleport 目标,直到应用程序的其他部分挂载(需要 Vue 3.5.0+)

reference

disabled
布尔值

禁用传送并内联渲染组件

reference

forceMount
布尔值

当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

to
字符串 | HTMLElement

Vue 原生传送组件属性 :to

reference

内容

浮动窗口打开时弹出的组件。

提示
使用 Presence 组件构建 - 支持任何 动画技术,同时保持对 Presence 发出事件的访问。
属性默认类型
align
'start' | 'center' | 'end'

相对于触发器的首选对齐方式。当发生碰撞时可能会改变。

alignOffset
数字

startend 对齐选项的像素偏移量。

arrowPadding
数字

箭头与内容边缘之间的内边距。如果您的内容有 border-radius,这将防止它溢出角落。

as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

avoidCollisions
布尔值

当为 true 时,覆盖侧边和对齐偏好,以防止与边界边缘发生碰撞。

collisionBoundary
Element | (Element | null)[] | null

用作碰撞边界的元素。默认情况下是视口,但您可以提供其他元素以包含在此检查中。

collisionPadding
数字 | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>

碰撞检测应发生的距边界边缘的像素距离。接受一个数字(所有边相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。

disableOutsidePointerEvents
布尔值

当为 true 时,DismissableLayer 外部元素的悬停/焦点/点击交互将被禁用。用户需要点击外部元素两次才能与之交互:第一次点击关闭 DismissableLayer,第二次点击触发该元素。

disableUpdateOnLayoutShift
布尔值

当布局发生偏移时,是否禁用更新内容位置。

forceMount
布尔值

当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

hideWhenDetached
布尔值

当触发器完全被遮挡时,是否隐藏内容。

positionStrategy
'fixed' | 'absolute'

要使用的 CSS position 属性类型。

prioritizePosition
布尔值

强制内容在视口内定位。

可能与参考元素重叠,这可能不是期望的结果。

reference
ReferenceElement

将设置为浮动元素定位参考的自定义元素或虚拟元素。

如果提供,它将替换默认的锚点元素。

side
'top' | 'right' | 'bottom' | 'left'

打开时,相对于触发器渲染的首选侧边。当发生碰撞且启用 avoidCollisions 时,将反转。

sideOffset
数字

与触发器的像素距离。

sticky
'部分' | '始终'

对齐轴上的粘性行为。partial 将使内容保持在边界内,只要触发器至少部分在边界内;而 "always" 则无论如何都会将内容保持在边界内。

updatePositionStrategy
'始终' | '优化'

在每个动画帧更新浮动元素位置的策略。

事件触发载荷
closeAutoFocus
[事件: Event]

关闭时自动聚焦调用的事件处理程序。可阻止其默认行为。

escapeKeyDown
[事件: KeyboardEvent]

按下 Esc 键时调用的事件处理程序。可阻止其默认行为。

focusOutside
[事件: FocusOutsideEvent]

当焦点移出 DismissableLayer 时调用的事件处理程序。可阻止其默认行为。

interactOutside
[事件: PointerDownOutsideEvent | FocusOutsideEvent]

DismissableLayer 外部发生交互时调用的事件处理程序。具体来说,当 pointerdown 事件在外部发生或焦点移出时。可阻止其默认行为。

openAutoFocus
[事件: Event]

打开时自动聚焦调用的事件处理程序。可阻止其默认行为。

pointerDownOutside
[事件: PointerDownOutsideEvent]

pointerdown 事件在 DismissableLayer 外部发生时调用的事件处理程序。可阻止其默认行为。

数据属性
[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 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

高度
5
数字

箭头的像素高度。

rounded
布尔值

当为 true 时,渲染圆角版本的箭头。不适用于 as/asChild

宽度
10
数字

箭头的像素宽度。

关闭

关闭已打开浮动窗口的按钮。

属性默认类型
as
'button'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

示例

限制内容大小

您可能希望限制内容的宽度以匹配触发器的宽度。您可能还希望限制其高度,使其不超过视口。

我们公开了一些 CSS 自定义属性,例如 --reka-popover-trigger-width--reka-popover-content-available-height 以支持此功能。使用它们来限制内容尺寸。

vue
<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>
css
/* styles.css */
.PopoverContent {
  width: var(--reka-popover-trigger-width);
  max-height: var(--reka-popover-content-available-height);
}

原点感知动画

我们公开了一个 CSS 自定义属性 --reka-popover-content-transform-origin。使用它来根据 sidesideOffsetalignalignOffset 和任何碰撞从其计算出的原点对内容进行动画处理。

vue
<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>
css
/* 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-sidedata-align 属性。它们的值将在运行时改变以反映碰撞。使用它们来创建碰撞和方向感知的动画。

vue
<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>
css
/* 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);
  }
}

带自定义锚点

如果您不想使用触发器作为锚点,可以将内容锚定到另一个元素。

vue
<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>
css
/* styles.css */
.Row {
  background-color: gainsboro;
  padding: 20px;
}

使用插槽属性关闭

或者,您可以使用 PopoverRoot 插槽属性提供的 close 方法以编程方式关闭浮动窗口。

vue
<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>

无障碍性

遵循 对话框 WAI-ARIA 设计模式

键盘交互

描述
空格键
打开/关闭浮动窗口。
回车键
打开/关闭浮动窗口。
Tab
将焦点移至下一个可聚焦元素
Shift + Tab
将焦点移至上一个可聚焦元素
Esc键
关闭浮动窗口并将焦点移至 PopoverTrigger

自定义 API

通过将原始部件抽象到您自己的组件中来创建您自己的 API。

抽象化箭头并设置默认配置

此示例抽象了 PopoverArrow 部分并设置了默认的 sideOffset 配置。

用法

vue
<script setup lang="ts">
import { Popover, PopoverContent, PopoverTrigger } from './your-popover'
</script>

<template>
  <Popover>
    <PopoverTrigger>Popover trigger</PopoverTrigger>
    <PopoverContent>Popover content</PopoverContent>
  </Popover>
</template>

实现

ts
// your-popover.ts
export { default as PopoverContent } from 'PopoverContent.vue'

export { PopoverRoot as Popover, PopoverTrigger } from 'reka-ui'
vue
<!-- 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>