Files
workspace/code/g3soft-libs/docs/content/ui/overlay/popover.md
T
2026-10-09 17:32:14 +08:00

5.2 KiB
Raw Blame History

title, description
title description
Popover 气泡卡片 浮层基座:12 方位定位、自动翻转、外部点击与 Esc 关闭

Popover 气泡卡片

Popover 是所有浮层的基础:Tooltip、Dropdown、Select、DatePicker 都建立在它之上。它负责定位、Teleport、关闭时机与滚动跟随,你只需要提供触发器和内容。

何时使用

  • 需要在触发器旁展示一段内容(说明、小表单、卡片)时;
  • 需要容器级别的自定义浮层时。更具体的场景请直接用上层封装:Tooltip、Dropdown、Select。

基础用法

#trigger 是触发器,#content 是浮层内容;默认 click 触发、显示箭头。

方位

placement 支持 12 个方位:top / bottom / left / right 各自带 -start / -end 对齐。空间不足时自动翻转到对面,再不够会沿视口边缘收缩(shift),始终保持 8px 安全边距。

触发方式

trigger 行为
click 点击触发器切换(默认)
hover 移入打开(mouseEnterDelay)、移出关闭(mouseLeaveDelay),移入浮层不会关闭
focus 聚焦打开,焦点移出关闭(焦点落在浮层内不关闭)
contextmenu 右键时在鼠标位置打开,越界自动回退
manual 组件不接管开关,完全由 open 控制

受控用法

传 open 即为受控:所有开关意图都会通过 update:open / openChange 抛出,你可以在里面做权限校验、埋点,或选择不关闭。

箭头与宽度

arrow=false 去掉尖角(菜单类浮层常用);followTriggerWidth 让浮层宽度以触发器宽度为下限(表单控件常用)。

API

Props

名称 类型 默认值 说明
open boolean — 受控打开状态;不传即非受控
defaultOpen boolean false 非受控时的初始状态
trigger 'click' | 'hover' | 'focus' | 'contextmenu' | 'manual' 'click' 触发方式
placement 'top' | 'top-start' | … | 'right-end' 'top' 期望方位(12 种),越界自动翻转 + 收缩
offset number 12 与触发器的间距(px)
arrow boolean true 是否显示箭头
disabled boolean false 禁用:不响应触发(受控 open 仍可强制打开)
mouseEnterDelay number 100 hover 打开延迟(ms)
mouseLeaveDelay number 100 hover 关闭延迟(ms)
contentClass string '' 透传到内容盒的 class,供派生组件定制内容样式
followTriggerWidth boolean false 浮层宽度以触发器宽度为下限
unwrapped boolean false 零包裹模式:把事件注入触发器单个根节点,不再包一层 <span>
popupContainer string | HTMLElement | (() => …) ConfigProvider 配置 浮层挂载容器,默认 body

Events

名称 参数 说明
update:open (open: boolean) 打开状态变化(受控与非受控都会触发)
openChange (open: boolean) 同上,语义别名

Slots

名称 参数 说明
trigger — 触发器内容。unwrapped 时要求单根元素
content — 浮层内容

类型定义

export type PopoverTrigger = 'click' | 'hover' | 'focus' | 'contextmenu' | 'manual'
export type Placement =
  | 'top' | 'top-start' | 'top-end'
  | 'bottom' | 'bottom-start' | 'bottom-end'
  | 'left' | 'left-start' | 'left-end'
  | 'right' | 'right-start' | 'right-end'

export interface PopoverProps {
  open?: boolean
  defaultOpen?: boolean
  trigger?: PopoverTrigger
  placement?: Placement
  offset?: number
  arrow?: boolean
  disabled?: boolean
  mouseEnterDelay?: number
  mouseLeaveDelay?: number
  contentClass?: string
  followTriggerWidth?: boolean
  unwrapped?: boolean
  popupContainer?: PopupContainer
}

实现说明

  • 定位:纯函数 computePosition 计算落点(先按方位摆放 → 主轴溢出则翻转 → 最后 clamp 回视口),浮层用 position: fixed + top/left,因此不会受父级 overflow: hidden 影响(配合 Teleport);
  • 跟随:打开期间监听捕获阶段的 scroll(覆盖内部滚动容器)、resize,并用 ResizeObserver 观察触发器与浮层尺寸,变化时重算(用 rAF 合并);
  • 层级:打开时从 z-index 注册表取一个 popup 档的值,关闭时回收(见 z-index 分层设计),所以弹窗里的下拉也能正确盖在弹窗之上;
  • unwrapped 模式的两个坑(已在实现里处理):① #trigger 里再嵌 <slot /> 会产出多层 Fragment,需要循环解包到真实元素;② 必须用渲染函数把注入了事件的 vnode 原样渲染,<component :is="vnode"> 会丢掉事件监听与指令;
  • 进出场只过渡 opacity / transform,不过渡 top/left(否则打开瞬间会从屏幕左上角滑过来)。