backdrop
组件

HoverCard

用于让有视力的用户在悬停时预览链接背后的内容。
Unovue

功能特点

  • 支持受控或非受控模式。
  • 可自定义方向、对齐方式、偏移量和碰撞处理。
  • 可选渲染指向箭头。
  • 支持自定义打开和关闭延迟。
  • 屏幕阅读器会忽略此组件。

安装

在命令行中安装该组件。

sh
$ npm add reka-ui

结构剖析

导入所有部件并将它们组合在一起。

vue
<script setup>
import { HoverCardArrow, HoverCardContent, HoverCardPortal, HoverCardRoot, HoverCardTrigger } from 'reka-ui'
</script>

<template>
  <HoverCardRoot>
    <HoverCardTrigger />
    <HoverCardPortal>
      <HoverCardContent>
        <HoverCardArrow />
      </HoverCardContent>
    </HoverCardPortal>
  </HoverCardRoot>
</template>

API 参考

Root

包含悬停卡片的所有部分。

PropDefaultType
closeDelay
300
number

The duration from when the mouse leaves the trigger or content until the hover card closes.

defaultOpen
false
boolean

The open state of the hover card when it is initially rendered. Use when you do not need to control its open state.

open
boolean

The controlled open state of the hover card. Can be binded as v-model:open.

openDelay
700
number

The duration from when the mouse enters the trigger until the hover card opens.

EmitPayload
update:open
[value: boolean]

Event handler called when the open state of the hover card changes.

Slots (default)Payload
open
boolean

Current open state

Trigger

悬停时打开悬停卡片的链接。

PropDefaultType
as
'a'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

reference
ReferenceElement

The reference (or anchor) element that is being referred to for positioning.

If not provided will use the current component as anchor.

Data AttributeValue
[data-state]"open" | "closed"

Portal

当使用时,会将内容部分挂载到 body 中。

PropDefaultType
defer
boolean

Defer the resolving of a Teleport target until other parts of the application have mounted (requires Vue 3.5.0+)

reference

disabled
boolean

Disable teleport and render the component inline

reference

forceMount
boolean

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

to
string | HTMLElement

Vue native teleport component prop :to

reference

Content

悬停卡片打开时弹出的组件。

tip
Built with Presence component - supports any animation techniques while maintaining access to presence emitted events.
PropDefaultType
align
'start' | 'center' | 'end'

The preferred alignment against the trigger. May change when collisions occur.

alignFlip
boolean

Flip alignment when colliding with boundary. May only occur when prioritizePosition is true.

alignOffset
number

An offset in pixels from the start or end alignment options.

arrowPadding
number

The 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 | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

avoidCollisions
boolean

When true, overrides the side and align preferences to prevent collisions with boundary edges.

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

The 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 }.

disableUpdateOnLayoutShift
boolean

Whether to disable the update position for the content when the layout shifted.

forceMount
boolean

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

hideWhenDetached
boolean

Whether to hide the content when the trigger becomes fully occluded.

positionStrategy
'fixed' | 'absolute'

The type of CSS position property to use.

prioritizePosition
boolean

Force content to be position within the viewport.

Might overlap the reference element, which may not be desired.

reference
ReferenceElement

The custom element or virtual element that will be set as the reference to position the floating element.

If provided, it will replace the default anchor element.

side
'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.

sideFlip
boolean

Flip to the opposite side when colliding with boundary.

sideOffset
number

The distance in pixels from the trigger.

sticky
'partial' | 'always'

The sticky behavior on the align axis. partial will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless.

updatePositionStrategy
'always' | 'optimized'

Strategy to update the position of the floating element on every animation frame.

EmitPayload
escapeKeyDown
[event: KeyboardEvent]

Event handler called when the escape key is down. Can be prevented.

focusOutside
[event: FocusOutsideEvent]

Event handler called when the focus moves outside of the DismissableLayer. Can be prevented.

interactOutside
[event: PointerDownOutsideEvent | FocusOutsideEvent]

Event handler called when an interaction happens outside the DismissableLayer. Specifically, when a pointerdown event happens outside or focus moves outside of it. Can be prevented.

pointerDownOutside
[event: PointerDownOutsideEvent]

Event handler called when a pointerdown event happens outside of the DismissableLayer. Can be prevented.

Data AttributeValue
[data-state]"open" | "closed"
[data-side]"left" | "right" | "bottom" | "top"
[data-align]"start" | "end" | "center"
CSS VariableDescription
--reka-hover-card-content-transform-origin
根据内容和箭头位置/偏移量计算出的 transform-origin
--reka-hover-card-content-available-width
触发器和边界边缘之间的剩余宽度
--reka-hover-card-content-available-height
触发器和边界边缘之间的剩余高度
--reka-hover-card-trigger-width
触发器的宽度
--reka-hover-card-trigger-height
触发器的高度

Arrow

一个可选的箭头元素,与悬停卡片一同渲染。可用于在视觉上连接触发器与 HoverCardContent。必须在 HoverCardContent 内部渲染。

PropDefaultType
as
'svg'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change 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
number

The height of the arrow in pixels.

rounded
boolean

When true, render the rounded version of arrow. Do not work with as/asChild

width
10
number

The width of the arrow in pixels.

示例

立即显示

使用 openDelay 属性来控制悬停卡片打开所需的时间。

vue
<script setup>
import {
  HoverCardArrow,
  HoverCardContent,
  HoverCardPortal,
  HoverCardRoot,
  HoverCardTrigger,
} from 'reka-ui'
</script>

<template>
  <HoverCardRoot :open-delay="0">
    <HoverCardTrigger>…</HoverCardTrigger>
    <HoverCardContent>…</HoverCardContent>
  </HoverCardRoot>
</template>

限制内容尺寸

你可能希望限制内容的宽度以匹配触发器的宽度,或者限制其高度不超出视口。

我们提供了几个 CSS 自定义属性,如 --reka-hover-card-trigger-width--reka-hover-card-content-available-height 来支持这一需求。使用它们来限制内容的尺寸。

vue
// index.vue
<script setup>
import { HoverCardArrow, HoverCardContent, HoverCardPortal, HoverCardRoot, HoverCardTrigger } from 'reka-ui'
</script>

<template>
  <HoverCardRoot>
    <HoverCardTrigger>…</HoverCardTrigger>
    <HoverCardPortal>
      <HoverCardContent
        class="HoverCardContent"
        :side-offset="5"
      >

      </HoverCardContent>
    </HoverCardPortal>
  </HoverCardRoot>
</template>
css
/* styles.css */
.HoverCardContent {
  width: var(--reka-hover-card-trigger-width);
  max-height: var(--reka-hover-card-content-available-height);
}

感知来源的动画

我们提供了一个 CSS 自定义属性 --reka-hover-card-content-transform-origin。使用它可以根据 sidesideOffsetalignalignOffset 以及任何碰撞情况,从计算出的原点对内容进行动画处理。

vue
<script setup>
import { HoverCardArrow, HoverCardContent, HoverCardPortal, HoverCardRoot, HoverCardTrigger } from 'reka-ui'
</script>

<template>
  <HoverCardRoot>
    <HoverCardTrigger>…</HoverCardTrigger>
    <HoverCardContent class="HoverCardContent">

    </HoverCardContent>
  </HoverCardRoot>
</template>
css
/* styles.css */
.HoverCardContent {
  transform-origin: var(--reka-hover-card-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 { HoverCardArrow, HoverCardContent, HoverCardPortal, HoverCardRoot, HoverCardTrigger } from 'reka-ui'
</script>

<template>
  <HoverCardRoot>
    <HoverCardTrigger>…</HoverCardTrigger>
    <HoverCardContent class="HoverCardContent">

    </HoverCardContent>
  </HoverCardRoot>
</template>
css
/* styles.css */
.HoverCardContent {
  animation-duration: 0.6s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.HoverCardContent[data-side="top"] {
  animation-name: slideUp;
}
.HoverCardContent[data-side="bottom"] {
  animation-name: slideDown;
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

可访问性

悬停卡片仅适用于有视力的用户,其内容对键盘用户不可访问。

键盘交互

KeyDescription
Tab
打开/关闭悬停卡片。
Enter
打开悬停卡片链接。