Tooltip
功能特性
- 提供全局控制显示延迟的 Provider。
- 在触发器获得焦点或悬停时打开。
- 在触发器被激活或按下 Escape 键时关闭。
- 支持自定义计时设置。
结构组成
导入所有部件并将其组合在一起。
<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 参考
Provider
包装您的应用,为工具提示提供全局功能。
| Prop | Default | Type |
|---|---|---|
delayDuration | 700 | numberThe duration from when the pointer enters the trigger until the tooltip gets opened. |
disableClosingTrigger | booleanWhen | |
disabled | booleanWhen | |
disableHoverableContent | false | booleanWhen |
ignoreNonKeyboardFocus | false | booleanPrevent the tooltip from opening if the focus did not come from
the keyboard by matching against the |
skipDelayDuration | 300 | numberHow much time a user has to enter another trigger without incurring a delay again. |
Root
包含工具提示的所有部分。
| Prop | Default | Type |
|---|---|---|
defaultOpen | false | booleanThe open state of the tooltip when it is initially rendered. Use when you do not need to control its open state. |
delayDuration | numberOverride the duration given to the | |
disableClosingTrigger | booleanWhen | |
disabled | booleanWhen | |
disableHoverableContent | booleanPrevents Tooltip.Content from remaining open when hovering. Disabling this has accessibility consequences. Inherits from Tooltip.Provider. | |
ignoreNonKeyboardFocus | booleanPrevent the tooltip from opening if the focus did not come from
the keyboard by matching against the | |
open | booleanThe controlled open state of the tooltip. |
| Emit | Payload |
|---|---|
update:open | [value: boolean]Event handler called when the open state of the tooltip changes. |
| Slots (default) | Payload |
|---|---|
open | booleanCurrent open state |
Trigger
用于切换工具提示的按钮。默认情况下,TooltipContent 将相对于触发器进行定位。
| Prop | Default | Type |
|---|---|---|
as | 'button' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
reference | ReferenceElementThe reference (or anchor) element that is being referred to for positioning. If not provided will use the current component as anchor. |
| Data Attribute | Value |
|---|---|
[data-state] | "closed" | "delayed-open" | "instant-open" |
Portal
使用时,将内容部分传送到 body 中。
| Prop | Default | Type |
|---|---|---|
defer | booleanDefer the resolving of a Teleport target until other parts of the application have mounted (requires Vue 3.5.0+) | |
disabled | booleanDisable teleport and render the component inline | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
to | string | HTMLElementVue native teleport component prop |
Content
工具提示打开时弹出的组件。
| Prop | Default | Type |
|---|---|---|
align | 'start' | 'center' | 'end'The preferred alignment against the trigger. May change when collisions occur. | |
alignOffset | numberAn offset in pixels from the | |
ariaLabel | stringBy default, screenreaders will announce the content inside the component. If this is not descriptive enough, or you have content that cannot be announced, use aria-label as a more descriptive label. | |
arrowPadding | numberThe padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. | |
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | booleanWhen | |
collisionBoundary | Element | (Element | null)[] | nullThe element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. | |
collisionPadding | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideWhenDetached | booleanWhether to hide the content when the trigger becomes fully occluded. | |
positionStrategy | 'fixed' | 'absolute'The type of CSS position property to use. | |
side | 'top' | 'top' | 'right' | 'bottom' | 'left'The preferred side of the trigger to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. |
sideOffset | numberThe distance in pixels from the trigger. | |
sticky | 'partial' | 'always'The sticky behavior on the align axis. | |
updatePositionStrategy | 'always' | 'optimized'Strategy to update the position of the floating element on every animation frame. |
| Emit | Payload |
|---|---|
escapeKeyDown | [event: KeyboardEvent]Event handler called when focus moves to the destructive action after opening. It can be prevented by calling |
pointerDownOutside | [event: Event]Event handler called when a pointer event occurs outside the bounds of the component. It can be prevented by calling |
| Data Attribute | Value |
|---|---|
[data-state] | "closed" | "delayed-open" | "instant-open" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
| CSS Variable | Description |
|---|---|
--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 | 触发器的高度 |
Arrow
一个可选的箭头元素,与工具提示一同渲染。可用于在视觉上连接触发器与 TooltipContent。必须在 TooltipContent 内部渲染。
| Prop | Default | Type |
|---|---|---|
as | 'svg' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
height | 5 | numberThe height of the arrow in pixels. |
width | 10 | numberThe width of the arrow in pixels. |
示例
全局配置
使用 Provider 全局控制 delayDuration 和 skipDelayDuration。
<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 属性控制工具提示打开所需的时间。
<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。
<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 来支持此功能。使用它们来约束内容尺寸。
<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>/* styles.css */
.TooltipContent {
width: var(--reka-tooltip-trigger-width);
max-height: var(--reka-tooltip-content-available-height);
}感知来源的动画
我们暴露了一个 CSS 自定义属性 --reka-tooltip-content-transform-origin。使用它可以根据 side、sideOffset、align、alignOffset 和任何碰撞,从计算出的原点对内容进行动画处理。
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>
<template>
<TooltipRoot>
<TooltipTrigger>…</TooltipTrigger>
<TooltipContent class="TooltipContent">
…
</TooltipContent>
</TooltipRoot>
</template>/* 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-side 和 data-align 属性。它们的值将在运行时改变以反映碰撞。使用它们来创建碰撞和方向感知的动画。
<script setup>
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui'
</script>
<template>
<TooltipRoot>
<TooltipTrigger>…</TooltipTrigger>
<TooltipContent class="TooltipContent">
…
</TooltipContent>
</TooltipRoot>
</template>/* 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);
}
}可访问性
键盘交互
| Key | Description |
|---|---|
Tab | 无延迟地打开/关闭工具提示。 |
Space | 如果已打开,则无延迟地关闭工具提示。 |
Enter | 如果已打开,则无延迟地关闭工具提示。 |
Escape | 如果已打开,则无延迟地关闭工具提示。 |
自定义 API
通过将原始部件抽象到您自己的组件中来创建您自己的 API。
抽象部件并引入内容属性
此示例抽象了所有 Tooltip 部件,并引入了一个新的 content 属性。
用法
<script setup lang="ts">
import { Tooltip } from './your-tooltip'
</script>
<template>
<Tooltip content="工具提示内容">
<button>工具提示触发器</button>
</Tooltip>
</template>实现
使用 asChild 属性 将触发器部分转换为可插槽区域。它将用传递给它的子项替换触发器。
<!-- 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>