Reka UI logoReka
backdrop
组件

工具提示

当元素获得键盘焦点或鼠标悬停在其上时,显示与该元素相关信息的弹出框。

功能

  • 提供全局控制显示延迟的功能。
  • 当触发器获得焦点或被悬停时打开。
  • 当触发器被激活或按下 Escape 键时关闭。
  • 支持自定义时间。

结构

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

vue
<script setup lang="ts">
import { TooltipArrow, TooltipContent, TooltipPortal, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipProvider>
    <TooltipRoot>
      <TooltipTrigger />
      <TooltipPortal>
        <TooltipContent>
          <TooltipArrow />
        </TooltipContent>
      </TooltipPortal>
    </TooltipRoot>
  </TooltipProvider>
</template>

API 参考

提供者

封装您的应用程序,为工具提示提供全局功能。

属性默认类型
延迟时间
700
数字

指针进入触发器到工具提示打开的持续时间。

禁用关闭触发器
布尔值

true 时,点击触发器将不会关闭内容。

禁用
布尔值

true 时,禁用工具提示

禁用可悬停内容
false
布尔值

true 时,尝试悬停内容将导致工具提示在指针离开触发器时关闭。

忽略非键盘焦点
false
布尔值

通过匹配 :focus-visible 选择器,防止工具提示在焦点不是来自键盘时打开。这在您希望避免在切换浏览器标签页或关闭对话框时打开工具提示时很有用。

跳过延迟时间
300
数字

用户在不再次产生延迟的情况下进入另一个触发器所需的时间。

包含工具提示的所有部分。

属性默认类型
默认打开
false
布尔值

工具提示首次渲染时的打开状态。当您不需要控制其打开状态时使用。

延迟时间
数字

覆盖提供给 Provider 的持续时间,以自定义特定工具提示的打开延迟。

禁用关闭触发器
布尔值

true 时,点击触发器将不会关闭内容。

禁用
布尔值

true 时,禁用工具提示

禁用可悬停内容
布尔值

防止 Tooltip.Content 在悬停时保持打开。禁用此功能会影响可访问性。继承自 Tooltip.Provider。

忽略非键盘焦点
布尔值

通过匹配 :focus-visible 选择器,防止工具提示在焦点不是来自键盘时打开。这在您希望避免在切换浏览器标签页或关闭对话框时打开工具提示时很有用。

打开
布尔值

工具提示的受控打开状态。

事件触发载荷
更新:打开
[value: boolean]

当工具提示的打开状态改变时调用的事件处理程序。

插槽(默认)载荷
打开
布尔值

当前打开状态

触发器

切换工具提示的按钮。默认情况下,TooltipContent 将根据触发器进行定位。

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

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

asChild
布尔值

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

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

引用
ReferenceElement

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

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

数据属性
[data-state]"closed" | "delayed-open" | "instant-open"

传送门

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

属性默认类型
延迟
布尔值

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

引用

禁用
布尔值

禁用传送并内联渲染组件

引用

强制挂载
布尔值

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

字符串 | HTMLElement

Vue 原生传送组件属性 :to

引用

内容

工具提示打开时弹出的组件。

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

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

对齐偏移
数字

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

aria标签
string

默认情况下,屏幕阅读器会朗读组件内部的内容。如果这不够具描述性,或者您有无法朗读的内容,请使用 aria-label 作为更具描述性的标签。

箭头内边距
数字

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

as
'div'
AsTag | Component

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

asChild
布尔值

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

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

避免碰撞
布尔值

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

碰撞边界
Element | (Element | null)[] | null

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

碰撞内边距
数字 | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>

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

强制挂载
布尔值

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

分离时隐藏
布尔值

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

定位策略
'fixed' | 'absolute'

要使用的 CSS position 属性类型。

侧边
'top'
'top' | 'right' | 'bottom' | 'left'

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

侧边偏移
数字

与触发器的像素距离。

粘性
'部分' | '始终'

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

更新定位策略
'始终' | '优化'

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

事件触发载荷
按下Escape键
[事件: KeyboardEvent]

在打开后焦点移动到破坏性操作时调用的事件处理程序。可以通过调用 event.preventDefault 来阻止此行为。

外部指针按下
[事件: Event]

当指针事件发生在组件边界之外时调用的事件处理程序。可以通过调用 event.preventDefault 来阻止此行为。

数据属性
[data-state]"closed" | "delayed-open" | "instant-open"
[data-side]"左" | "右" | "下" | "上"
[data-align]"开始" | "结束" | "居中"
CSS 变量描述
--reka-tooltip-content-transform-origin
从内容和箭头位置/偏移量计算的 transform-origin
--reka-tooltip-content-available-width
触发器与边界边缘之间的剩余宽度
--reka-tooltip-content-available-height
触发器与边界边缘之间的剩余高度
--reka-tooltip-trigger-width
触发器的宽度
--reka-tooltip-trigger-height
触发器的高度

箭头

一个可选的箭头元素,与工具提示一同渲染。这可用于帮助视觉上将触发器与 TooltipContent 关联起来。必须在 TooltipContent 内部渲染。

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

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

asChild
布尔值

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

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

高度
5
数字

箭头的像素高度。

宽度
10
数字

箭头的像素宽度。

示例

全局配置

使用 Provider 全局控制 delayDurationskipDelayDuration

vue
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipProvider
    :delay-duration="800"
    :skip-delay-duration="500"
  >
    <TooltipRoot>
      <TooltipTrigger>…</TooltipTrigger>
      <TooltipContent>…</TooltipContent>
    </TooltipRoot>
    <TooltipRoot>
      <TooltipTrigger>…</TooltipTrigger>
      <TooltipContent>…</TooltipContent>
    </TooltipRoot>
  </TooltipProvider>
</template>

立即显示

使用 delayDuration 属性控制工具提示打开所需的时间。

vue
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipRoot :delay-duration="0">
    <TooltipTrigger>…</TooltipTrigger>
    <TooltipContent>…</TooltipContent>
  </TooltipRoot>
</template>

从禁用按钮显示工具提示

由于禁用按钮不触发事件,您需要

  • Trigger 渲染为 span
  • 确保 button 没有 pointerEvents
vue
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipRoot>
    <TooltipTrigger as-child>
      <span tabindex="0">
        <button
          disabled
          style="{ pointerEvents: 'none' }"
        >…</button>
      </span>
    </TooltipTrigger>
    <TooltipContent>…</TooltipContent>
  </TooltipRoot>
</template>

限制内容尺寸

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

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

vue
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipRoot>
    <TooltipTrigger>…</TooltipTrigger>
    <TooltipPortal>
      <TooltipContent
        class="TooltipContent"
        :side-offset="5"
      >

      </TooltipContent>
    </TooltipPortal>
  </TooltipRoot>
</template>
css
/* styles.css */
.TooltipContent {
  width: var(--reka-tooltip-trigger-width);
  max-height: var(--reka-tooltip-content-available-height);
}

感知原点的动画

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

vue
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipRoot>
    <TooltipTrigger>…</TooltipTrigger>
    <TooltipContent class="TooltipContent">

    </TooltipContent>
  </TooltipRoot>
</template>
css
/* styles.css */
.TooltipContent {
  transform-origin: var(--reka-tooltip-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 { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>

<template>
  <TooltipRoot>
    <TooltipTrigger>…</TooltipTrigger>
    <TooltipContent class="TooltipContent">

    </TooltipContent>
  </TooltipRoot>
</template>
css
/* styles.css */
.TooltipContent {
  animation-duration: 0.6s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.TooltipContent[data-side="top"] {
  animation-name: slideUp;
}
.TooltipContent[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);
  }
}

可访问性

键盘交互

描述
Tab
立即打开/关闭工具提示。
空格键
如果已打开,则立即关闭工具提示。
回车键
如果已打开,则立即关闭工具提示。
Escape
如果已打开,则立即关闭工具提示。

自定义 API

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

抽象组件并引入内容属性

此示例抽象了所有 Tooltip 组件并引入了一个新的 content 属性。

用法

vue
<script setup lang="ts">
import { Tooltip } from './your-tooltip'
</script>

<template>
  <Tooltip content="Tooltip content">
    <button>Tooltip trigger</button>
  </Tooltip>
</template>

实现

使用 asChild 属性 将触发器部分转换为可插槽区域。它将用传递给它的子元素替换触发器。

vue
<!-- your-tooltip.vue  -->
<script setup lang="ts">
import type { TooltipRootEmits, TooltipRootProps } from 'reka-ui'
import { TooltipArrow, TooltipContent, TooltipRoot, TooltipTrigger, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<TooltipRootProps & { content?: string }>()
const emits = defineEmits<TooltipRootEmits>()

const forward = useForwardPropsEmits(props, emits)
</script>

<template>
  <TooltipRoot v-bind="forward">
    <TooltipTrigger as-child>
      <slot />
    </TooltipTrigger>
    <TooltipContent
      side="top"
      align="center"
    >
      {{ content }}
      <TooltipArrow
        :width="11"
        :height="5"
      />
    </TooltipContent>
  </TooltipRoot>
</template>